Files
zopu-code/packages/agents/src/orb/context-pack.ts
T
-Puter 50ccf4e418 feat(agents/orb): wire project-manager orchestration over merged Orb runtime
Add a thin orchestration layer that drives the existing Orb runtime to
progress issue-scoped work. OrbProjectManager creates/resumes Orb runs,
assembles context packs, projects OrbEvents into durable project events,
detects needs-input and work-complete conditions, forwards follow-up
messages, and runs the Git/Gitea lifecycle on completion.

Port interfaces (OrbAdapter, OrbRunPort, GitLifecyclePort) make the
orchestrator testable with fakes. The real adapters wrap OrbRuntime and
runPostRunGiteaLifecycle without reimplementing Docker, AgentOS, or
OpenCode.

Idempotency: duplicate starts reuse active runs, duplicate completion
returns cached results, cancel is idempotent. Never auto-merges.

88 tests pass (19 orchestration + 12 event-mapping + orb suite), 2 live
tests skipped; lint and type checks clean.
2026-07-25 01:53:16 +05:30

110 lines
3.4 KiB
TypeScript

import { Schema } from "effect";
// ---------------------------------------------------------------------------
// Context pack — assembled from ProjectIssue, evidence, project docs, artifacts
// ---------------------------------------------------------------------------
export const ContextFile = Schema.Struct({
content: Schema.String,
label: Schema.String,
});
export type ContextFile = typeof ContextFile.Type;
export const ContextPackInput = Schema.Struct({
artifacts: Schema.Array(
Schema.Struct({
content: Schema.String,
path: Schema.String,
})
),
contextFiles: Schema.Array(ContextFile),
evidence: Schema.Array(
Schema.Struct({
content: Schema.String,
source: Schema.String,
})
),
issueBody: Schema.String,
issueNumber: Schema.Int,
issueTitle: Schema.String,
repositoryMetadata: Schema.Struct({
baseBranch: Schema.String,
repositoryName: Schema.String,
repositoryUrl: Schema.String,
}),
});
export type ContextPackInput = typeof ContextPackInput.Type;
const section = (heading: string, lines: readonly string[]): string => {
if (lines.length === 0) {
return "";
}
return `## ${heading}\n\n${lines.join("\n")}`;
};
const OPERATIONAL_INSTRUCTIONS = `## Operational Instructions
You are working inside an isolated sandbox on a dedicated work branch. The repository checkout is your working directory.
1. Inspect existing source before changing anything.
2. Implement the complete issue scope. Run the project's test or verification command.
3. If you need human input to proceed safely, emit exactly one line starting with \`NEEDS_INPUT:\` followed by your question, then stop.
4. When implementation and verification are complete, emit exactly one line starting with \`WORK_COMPLETE:\` followed by a one-sentence summary, then stop.
5. Do not merge, deploy, or push. The orchestrator handles the Git lifecycle after you signal completion.
6. Never claim a change you did not observe. Preserve command evidence.`;
/**
* Build a concise context pack prompt for the OpenCode session. The pack is
* sent as the first task and includes: issue details, project docs, evidence,
* prior artifacts, repository metadata, and operational instructions with the
* needs-input / work-complete marker protocol.
*/
export const buildContextPack = (input: ContextPackInput): string => {
const parts: string[] = [
`# Issue #${input.issueNumber}: ${input.issueTitle}\n\n${input.issueBody}`,
];
if (input.contextFiles.length > 0) {
parts.push(
section(
"Project Context",
input.contextFiles.map(
(file) => `### ${file.label}\n\n\`\`\`\n${file.content}\n\`\`\``
)
)
);
}
if (input.evidence.length > 0) {
parts.push(
section(
"Supporting Evidence",
input.evidence.map((item) => `- **${item.source}**: ${item.content}`)
)
);
}
if (input.artifacts.length > 0) {
parts.push(
section(
"Previous Work Artifacts",
input.artifacts.map(
(artifact) =>
`### ${artifact.path}\n\n\`\`\`\n${artifact.content}\n\`\`\``
)
)
);
}
parts.push(
section("Repository", [
`- Name: ${input.repositoryMetadata.repositoryName}`,
`- URL: ${input.repositoryMetadata.repositoryUrl}`,
`- Base branch: ${input.repositoryMetadata.baseBranch}`,
]),
OPERATIONAL_INSTRUCTIONS
);
return parts.filter((part) => part.length > 0).join("\n\n---\n\n");
};