@synap-core/cli 1.13.0 → 1.13.1-next.20261102.3a66eb1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -3
- package/dist/build-info.json +2 -3
- package/dist/commands/agents.js +12 -2
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/capability.d.ts +86 -0
- package/dist/commands/capability.js +117 -30
- package/dist/commands/capability.js.map +1 -1
- package/dist/commands/context.js +2 -1
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/data-extra.d.ts +13 -0
- package/dist/commands/data-extra.js +23 -2
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/doctor.d.ts +18 -0
- package/dist/commands/doctor.js +28 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.js +4 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/keys-request.d.ts +48 -0
- package/dist/commands/keys-request.js +236 -0
- package/dist/commands/keys-request.js.map +1 -0
- package/dist/commands/keys-request.test.d.ts +1 -0
- package/dist/commands/keys-request.test.js +176 -0
- package/dist/commands/keys-request.test.js.map +1 -0
- package/dist/commands/knowledge.d.ts +42 -5
- package/dist/commands/knowledge.js +90 -27
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/market.d.ts +89 -31
- package/dist/commands/market.js +215 -222
- package/dist/commands/market.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -1
- package/dist/commands/mcp.js +14 -9
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/pods.js +6 -2
- package/dist/commands/pods.js.map +1 -1
- package/dist/commands/proposals-actions.js +7 -1
- package/dist/commands/proposals-actions.js.map +1 -1
- package/dist/commands/providers.d.ts +22 -6
- package/dist/commands/providers.js +39 -32
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/raycast.d.ts +15 -0
- package/dist/commands/raycast.js +36 -10
- package/dist/commands/raycast.js.map +1 -1
- package/dist/commands/status.js +12 -6
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/statusline.backoff.test.js +13 -3
- package/dist/commands/statusline.backoff.test.js.map +1 -1
- package/dist/commands/statusline.d.ts +104 -9
- package/dist/commands/statusline.js +392 -148
- package/dist/commands/statusline.js.map +1 -1
- package/dist/commands/statusline.line2.test.d.ts +15 -0
- package/dist/commands/statusline.line2.test.js +226 -0
- package/dist/commands/statusline.line2.test.js.map +1 -0
- package/dist/commands/statusline.needs-you.test.d.ts +1 -0
- package/dist/commands/statusline.needs-you.test.js +17 -0
- package/dist/commands/statusline.needs-you.test.js.map +1 -0
- package/dist/commands/statusline.now.test.d.ts +10 -0
- package/dist/commands/statusline.now.test.js +250 -0
- package/dist/commands/statusline.now.test.js.map +1 -0
- package/dist/commands/tracks.d.ts +1 -0
- package/dist/commands/tracks.js +4 -0
- package/dist/commands/tracks.js.map +1 -1
- package/dist/commands/upload.js +12 -0
- package/dist/commands/upload.js.map +1 -1
- package/dist/commands/wait.d.ts +124 -0
- package/dist/commands/wait.js +384 -0
- package/dist/commands/wait.js.map +1 -0
- package/dist/commands/wait.test.d.ts +8 -0
- package/dist/commands/wait.test.js +308 -0
- package/dist/commands/wait.test.js.map +1 -0
- package/dist/commands/worker-github-app.d.ts +63 -0
- package/dist/commands/worker-github-app.js +161 -0
- package/dist/commands/worker-github-app.js.map +1 -0
- package/dist/commands/worker-github-app.test.d.ts +1 -0
- package/dist/commands/worker-github-app.test.js +102 -0
- package/dist/commands/worker-github-app.test.js.map +1 -0
- package/dist/commands/worker-github-broker.d.ts +39 -0
- package/dist/commands/worker-github-broker.js +106 -0
- package/dist/commands/worker-github-broker.js.map +1 -0
- package/dist/commands/worker-github-broker.test.d.ts +1 -0
- package/dist/commands/worker-github-broker.test.js +90 -0
- package/dist/commands/worker-github-broker.test.js.map +1 -0
- package/dist/commands/worker-runs.test.d.ts +1 -0
- package/dist/commands/worker-runs.test.js +150 -0
- package/dist/commands/worker-runs.test.js.map +1 -0
- package/dist/commands/worker-worktrees.d.ts +34 -0
- package/dist/commands/worker-worktrees.js +87 -0
- package/dist/commands/worker-worktrees.js.map +1 -0
- package/dist/commands/worker.d.ts +207 -5
- package/dist/commands/worker.js +540 -46
- package/dist/commands/worker.js.map +1 -1
- package/dist/commands/worker.test.js +269 -3
- package/dist/commands/worker.test.js.map +1 -1
- package/dist/index.js +60 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/agents-config.d.ts +9 -0
- package/dist/lib/agents-config.js +20 -0
- package/dist/lib/agents-config.js.map +1 -1
- package/dist/lib/atomic-file.d.ts +20 -1
- package/dist/lib/atomic-file.js +100 -2
- package/dist/lib/atomic-file.js.map +1 -1
- package/dist/lib/auth.d.ts +25 -0
- package/dist/lib/auth.js +49 -9
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/box/job-server.js +4 -0
- package/dist/lib/box/job-server.js.map +1 -1
- package/dist/lib/bridge-invocation.test.d.ts +1 -0
- package/dist/lib/bridge-invocation.test.js +75 -0
- package/dist/lib/bridge-invocation.test.js.map +1 -0
- package/dist/lib/bridge-status.d.ts +144 -0
- package/dist/lib/bridge-status.js +227 -0
- package/dist/lib/bridge-status.js.map +1 -0
- package/dist/lib/bridge-status.test.d.ts +6 -0
- package/dist/lib/bridge-status.test.js +197 -0
- package/dist/lib/bridge-status.test.js.map +1 -0
- package/dist/lib/client-header.test.js +19 -1
- package/dist/lib/client-header.test.js.map +1 -1
- package/dist/lib/conversation-headers.d.ts +67 -2
- package/dist/lib/conversation-headers.js +122 -6
- package/dist/lib/conversation-headers.js.map +1 -1
- package/dist/lib/conversation-headers.test.d.ts +1 -0
- package/dist/lib/conversation-headers.test.js +78 -0
- package/dist/lib/conversation-headers.test.js.map +1 -0
- package/dist/lib/credential-class.d.ts +12 -1
- package/dist/lib/credential-class.js +20 -0
- package/dist/lib/credential-class.js.map +1 -1
- package/dist/lib/device-connect.d.ts +45 -0
- package/dist/lib/device-connect.js +60 -0
- package/dist/lib/device-connect.js.map +1 -1
- package/dist/lib/goal-words.js +6 -3
- package/dist/lib/goal-words.js.map +1 -1
- package/dist/lib/hub-client.d.ts +59 -1
- package/dist/lib/hub-client.js +130 -36
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/key-locations.d.ts +3 -3
- package/dist/lib/key-locations.js +3 -39
- package/dist/lib/key-locations.js.map +1 -1
- package/dist/lib/machine-instance.d.ts +7 -5
- package/dist/lib/machine-instance.js +7 -5
- package/dist/lib/machine-instance.js.map +1 -1
- package/dist/lib/mcp-bridge.d.ts +43 -0
- package/dist/lib/mcp-bridge.js +199 -11
- package/dist/lib/mcp-bridge.js.map +1 -1
- package/dist/lib/mcp-bridge.test.js +194 -2
- package/dist/lib/mcp-bridge.test.js.map +1 -1
- package/dist/lib/mcp-call.d.ts +5 -1
- package/dist/lib/mcp-call.js +7 -6
- package/dist/lib/mcp-call.js.map +1 -1
- package/dist/lib/mcp-connector-url.test.d.ts +1 -0
- package/dist/lib/mcp-connector-url.test.js +13 -0
- package/dist/lib/mcp-connector-url.test.js.map +1 -0
- package/dist/lib/mcp-snippets.d.ts +16 -0
- package/dist/lib/mcp-snippets.js +20 -2
- package/dist/lib/mcp-snippets.js.map +1 -1
- package/dist/lib/now-line.d.ts +66 -0
- package/dist/lib/now-line.js +193 -0
- package/dist/lib/now-line.js.map +1 -0
- package/dist/lib/now-line.test.d.ts +5 -0
- package/dist/lib/now-line.test.js +177 -0
- package/dist/lib/now-line.test.js.map +1 -0
- package/dist/lib/package-updates.d.ts +66 -0
- package/dist/lib/package-updates.js +76 -0
- package/dist/lib/package-updates.js.map +1 -0
- package/dist/lib/pod.d.ts +0 -4
- package/dist/lib/pod.js +0 -7
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/register-claude-code-mcp.test.js +19 -0
- package/dist/lib/register-claude-code-mcp.test.js.map +1 -1
- package/dist/lib/safe-open.d.ts +5 -3
- package/dist/lib/safe-open.js +9 -8
- package/dist/lib/safe-open.js.map +1 -1
- package/dist/lib/self-host.d.ts +17 -0
- package/dist/lib/self-host.js +24 -0
- package/dist/lib/self-host.js.map +1 -0
- package/dist/lib/targets.d.ts +31 -0
- package/dist/lib/targets.js +100 -104
- package/dist/lib/targets.js.map +1 -1
- package/dist/lib/template-lifecycle.d.ts +0 -31
- package/dist/lib/template-lifecycle.js +0 -74
- package/dist/lib/template-lifecycle.js.map +1 -1
- package/dist/lib/term.d.ts +24 -0
- package/dist/lib/term.js +52 -0
- package/dist/lib/term.js.map +1 -0
- package/dist/lib/term.test.d.ts +1 -0
- package/dist/lib/term.test.js +29 -0
- package/dist/lib/term.test.js.map +1 -0
- package/dist/lib/transcript-index.d.ts +94 -0
- package/dist/lib/transcript-index.js +321 -0
- package/dist/lib/transcript-index.js.map +1 -0
- package/dist/lib/transcript-index.test.d.ts +10 -0
- package/dist/lib/transcript-index.test.js +200 -0
- package/dist/lib/transcript-index.test.js.map +1 -0
- package/dist/lib/wait-help.d.ts +11 -0
- package/dist/lib/wait-help.js +25 -0
- package/dist/lib/wait-help.js.map +1 -0
- package/dist/lib/wait-state.d.ts +57 -0
- package/dist/lib/wait-state.js +114 -0
- package/dist/lib/wait-state.js.map +1 -0
- package/package.json +2 -2
- package/skills/synap/SKILL.md +81 -44
- package/skills/synap/code-skills.md +15 -1
- package/skills/synap/concepts.md +36 -26
- package/skills/synap/document-embeds.md +15 -1
- package/skills/synap/focus-sessions.md +4 -4
- package/skills/synap/from-intent.md +2 -2
- package/skills/synap/governance-rules.md +2 -0
- package/skills/synap/hand-off.md +10 -6
- package/skills/synap/reflexes.md +4 -4
- package/skills/synap/work-model.md +1 -1
- package/skills/synap/writing-titles.md +7 -0
- package/skills/synap-app/SKILL.md +2 -2
- package/skills/synap-app/overview.md +2 -2
- package/skills/synap-market/SKILL.md +1 -1
- package/skills/synap-market/governance-and-catalog.md +1 -1
- package/skills/synap-schema/SKILL.md +4 -1
- package/skills/synap-schema/intent-vocabulary-ssot.md +4 -1
- package/dist/commands/template.d.ts +0 -42
- package/dist/commands/template.js +0 -191
- package/dist/commands/template.js.map +0 -1
- package/dist/lib/browser-auth.d.ts +0 -35
- package/dist/lib/browser-auth.js +0 -170
- package/dist/lib/browser-auth.js.map +0 -1
- package/dist/lib/openclaw.d.ts +0 -32
- package/dist/lib/openclaw.js +0 -165
- package/dist/lib/openclaw.js.map +0 -1
- package/dist/lib/seed.d.ts +0 -13
- package/dist/lib/seed.js +0 -135
- package/dist/lib/seed.js.map +0 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One tree folder per code run, so two runs at once never share a checkout
|
|
3
|
+
* (finding f94c0287: both worked in the one clone under `--cwd`).
|
|
4
|
+
*
|
|
5
|
+
* The worker's `--cwd` is usually a folder OF repos, not a repo, so the worker
|
|
6
|
+
* cannot create the worktree itself: it gives the run an empty folder
|
|
7
|
+
* (`<state>/trees/<short id>`) as its cwd, the prompt tells the run to make its
|
|
8
|
+
* worktrees there (`git -C <cwd>/<repo> worktree add <folder>/<repo> …`), and
|
|
9
|
+
* when the run ends the worker sweeps the folder: a worktree with no
|
|
10
|
+
* uncommitted change and no commit that is not on a remote is removed; any
|
|
11
|
+
* other is KEPT, and named in the run's result.
|
|
12
|
+
*/
|
|
13
|
+
import { execFile } from "node:child_process";
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
export const defaultGit = (cwd, args) => new Promise((resolve) => {
|
|
17
|
+
execFile("git", args, { cwd, timeout: 60_000, maxBuffer: 16 * 1024 * 1024 }, (err, stdout) => {
|
|
18
|
+
const code = err ? (typeof err.code === "number" ? err.code : 1) : 0;
|
|
19
|
+
resolve({ code, stdout: String(stdout ?? "") });
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
export function treeFolderFor(treesRoot, sessionId) {
|
|
23
|
+
return path.join(treesRoot, sessionId.slice(0, 8));
|
|
24
|
+
}
|
|
25
|
+
/** The run's own folder under the trees root; created owner-only. */
|
|
26
|
+
export function ensureTreeFolder(folder) {
|
|
27
|
+
fs.mkdirSync(folder, { recursive: true, mode: 0o700 });
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Removes the run's clean worktrees. A failed check is a reason to KEEP, never
|
|
31
|
+
* "clean": an unreadable status must not delete work.
|
|
32
|
+
*/
|
|
33
|
+
export async function sweepTreeFolder(folder, git = defaultGit) {
|
|
34
|
+
const out = { removed: [], kept: [] };
|
|
35
|
+
let entries;
|
|
36
|
+
try {
|
|
37
|
+
entries = fs.readdirSync(folder, { withFileTypes: true });
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
for (const e of entries) {
|
|
43
|
+
const dir = path.join(folder, e.name);
|
|
44
|
+
if (!e.isDirectory())
|
|
45
|
+
continue;
|
|
46
|
+
if (!fs.existsSync(path.join(dir, ".git"))) {
|
|
47
|
+
out.kept.push({ dir, why: "not a git worktree" });
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const status = await git(dir, ["status", "--porcelain"]);
|
|
51
|
+
if (status.code !== 0) {
|
|
52
|
+
out.kept.push({ dir, why: "git status failed" });
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (status.stdout.trim()) {
|
|
56
|
+
out.kept.push({ dir, why: "uncommitted changes" });
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
// Commits reachable from HEAD that no remote-tracking ref has: unpushed work.
|
|
60
|
+
const unpushed = await git(dir, ["rev-list", "--count", "HEAD", "--not", "--remotes"]);
|
|
61
|
+
if (unpushed.code !== 0 || Number(unpushed.stdout.trim()) !== 0) {
|
|
62
|
+
out.kept.push({ dir, why: unpushed.code !== 0 ? "git rev-list failed" : "unpushed commits" });
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const common = await git(dir, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
|
|
66
|
+
if (common.code !== 0) {
|
|
67
|
+
out.kept.push({ dir, why: "git-common-dir failed" });
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
// Run from the main repo: git refuses to remove the worktree it is run in.
|
|
71
|
+
const removed = await git(path.dirname(common.stdout.trim()), ["worktree", "remove", dir]);
|
|
72
|
+
if (removed.code !== 0)
|
|
73
|
+
out.kept.push({ dir, why: "git worktree remove refused" });
|
|
74
|
+
else
|
|
75
|
+
out.removed.push(dir);
|
|
76
|
+
}
|
|
77
|
+
if (!out.kept.length) {
|
|
78
|
+
try {
|
|
79
|
+
fs.rmdirSync(folder);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
/* not empty: something else is in it, leave it */
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=worker-worktrees.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-worktrees.js","sourceRoot":"","sources":["../../src/commands/worker-worktrees.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,8 +41,13 @@
|
|
|
41
41
|
* State (which sessions were launched, their Claude session ids and outcome)
|
|
42
42
|
* lives in ~/.synap/worker/<agent>/state.json; each run's stream-json goes to
|
|
43
43
|
* ~/.synap/worker/<agent>/runs/<sessionId>.jsonl. Neither ever holds a token.
|
|
44
|
+
* A run's verdict is ALSO recorded on its session (`settle`), and the worker's
|
|
45
|
+
* orient poll carries its served stamp (`X-Synap-Runner`): the pod's proof
|
|
46
|
+
* that a process serves this agent.
|
|
44
47
|
*/
|
|
45
48
|
import { type McpCallClient } from "../lib/mcp-call.js";
|
|
49
|
+
import { type GithubRunEnv, type InstallationTokenSource } from "./worker-github-app.js";
|
|
50
|
+
import { type GitFn } from "./worker-worktrees.js";
|
|
46
51
|
export interface WorkerOptions {
|
|
47
52
|
agent: string;
|
|
48
53
|
interval?: string;
|
|
@@ -56,12 +61,27 @@ export interface WorkerOptions {
|
|
|
56
61
|
dryRun?: boolean;
|
|
57
62
|
/** The Claude accounts file (default ~/.config/synap-worker/accounts.json, used when it exists). */
|
|
58
63
|
accounts?: string;
|
|
64
|
+
/** The GitHub App env file ({GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY_FILE}; default ~/.config/synap-agent-app.env when it exists). */
|
|
65
|
+
githubApp?: string;
|
|
59
66
|
/** Where work-folder runs get their folder, one per session (default ~/synap-work). */
|
|
60
67
|
workRoot?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Which protocols this worker takes, comma-separated (`code`, `work-folder`;
|
|
70
|
+
* default both). A design run counts as `code`. A computer that runs no code
|
|
71
|
+
* (the person's Mac) takes `work-folder` only (`parseProtocols`).
|
|
72
|
+
*/
|
|
73
|
+
protocols?: string;
|
|
61
74
|
}
|
|
62
75
|
export interface HandedItem {
|
|
63
76
|
id: string;
|
|
64
77
|
goal: string | null;
|
|
78
|
+
/**
|
|
79
|
+
* A session that went quiet (stale) while handed to this agent, handed back
|
|
80
|
+
* to be RESUMED: it never launches fresh. Absent from an older pod's items.
|
|
81
|
+
*/
|
|
82
|
+
resume?: true;
|
|
83
|
+
/** When it went quiet (ISO), with `resume`. */
|
|
84
|
+
quietSince?: string;
|
|
65
85
|
}
|
|
66
86
|
/** `limited`: cut off by a usage limit, waiting to be resumed on another account. */
|
|
67
87
|
export type RunStatus = "running" | "done" | "failed" | "limited";
|
|
@@ -94,6 +114,40 @@ export interface RunRecord {
|
|
|
94
114
|
protocol?: WorkerProtocol;
|
|
95
115
|
/** A work-folder run's folder: its cwd, which every resume reuses (Claude finds a conversation by its cwd). */
|
|
96
116
|
workDir?: string;
|
|
117
|
+
/** A code run's own folder for its worktrees: its cwd, which every resume reuses. */
|
|
118
|
+
treeDir?: string;
|
|
119
|
+
/** Worktrees a finished run left because they hold unpushed or uncommitted work. */
|
|
120
|
+
keptTrees?: string[];
|
|
121
|
+
/** The latest launch's process identity (`X-Synap-Process`), what its runner row is keyed by. */
|
|
122
|
+
process?: string;
|
|
123
|
+
/** The model the run's Claude Code said it runs (its stream's `init`), reported as `X-Synap-Model`. */
|
|
124
|
+
model?: string;
|
|
125
|
+
/** The quiet period (its `quietSince`, "" when the pod gave none) this run was last resumed for. */
|
|
126
|
+
resumedQuietSince?: string;
|
|
127
|
+
/** The latest launch's end, not yet recorded on the pod (`settle`): retried each tick until it lands. */
|
|
128
|
+
unsettled?: PendingSettle;
|
|
129
|
+
/** When the latest end was recorded on the pod. */
|
|
130
|
+
settledAt?: string;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A run's end the pod has not heard yet, saved before the first try so that a
|
|
134
|
+
* crash or an MCP blip between the local save and the pod write loses nothing:
|
|
135
|
+
* the next tick (of this worker, or of the next one after a restart) retries it.
|
|
136
|
+
* It names its own launch, so a later resume of the same session never changes
|
|
137
|
+
* which runner it ends or which Result post it is.
|
|
138
|
+
*/
|
|
139
|
+
export interface PendingSettle {
|
|
140
|
+
/** The launch's process identity and conversation: what the pod's runner row is keyed by. */
|
|
141
|
+
process?: string;
|
|
142
|
+
claudeSessionId?: string;
|
|
143
|
+
/** The verdict to record; absent: the pod only hears the runner ended. */
|
|
144
|
+
verdict?: {
|
|
145
|
+
status: VerdictStatus;
|
|
146
|
+
sentence: string;
|
|
147
|
+
};
|
|
148
|
+
/** The Result post's `idempotencyKey`: a retry returns the post that already landed. */
|
|
149
|
+
key: string;
|
|
150
|
+
attempts: number;
|
|
97
151
|
}
|
|
98
152
|
interface WorkerState {
|
|
99
153
|
runs: Record<string, RunRecord>;
|
|
@@ -129,7 +183,11 @@ export declare function parseHandedToYou(orientText: string): HandedItem[];
|
|
|
129
183
|
*/
|
|
130
184
|
export declare const LAUNCH_RETRY_BASE_MS: number;
|
|
131
185
|
export declare const MAX_LAUNCH_RETRIES = 4;
|
|
132
|
-
/**
|
|
186
|
+
/**
|
|
187
|
+
* Sessions to launch now: never launched, or due a relaunch (see above). A
|
|
188
|
+
* `resume` item is never one of them: it continues its conversation or waits
|
|
189
|
+
* (see `tick`), so an empty state file never relaunches every quiet session.
|
|
190
|
+
*/
|
|
133
191
|
export declare function pickNew(items: HandedItem[], state: WorkerState, slots: number, now?: number): HandedItem[];
|
|
134
192
|
/**
|
|
135
193
|
* The pod MCP server every run gets, under this name, carrying the WORKER
|
|
@@ -139,11 +197,36 @@ export declare function pickNew(items: HandedItem[], state: WorkerState, slots:
|
|
|
139
197
|
* then read its own posts as follow-ups (2026-10-20).
|
|
140
198
|
*/
|
|
141
199
|
export declare const RUN_MCP_SERVER = "synap-worker";
|
|
200
|
+
/**
|
|
201
|
+
* The provider every run's model is served by: Anthropic, on the account's
|
|
202
|
+
* subscription. By construction of the run's environment (`workerEnv`): no
|
|
203
|
+
* `ANTHROPIC_*` (key, token, base URL) and no provider switch
|
|
204
|
+
* (`RUN_ENV_BLOCKLIST`) reaches it. Reported as `X-Synap-Model-Provider`.
|
|
205
|
+
*/
|
|
206
|
+
export declare const WORKER_MODEL_PROVIDER = "anthropic";
|
|
142
207
|
export declare function runMcpConfig(podUrl: string, apiKey: string,
|
|
143
208
|
/** The run's Claude session id: names the conversation to the pod, one id across resumes. */
|
|
144
|
-
claudeSessionId?: string
|
|
209
|
+
claudeSessionId?: string,
|
|
210
|
+
/** The launch's process identity (`runProcessIdentity`): the pod keeps one runner row per process. */
|
|
211
|
+
processIdentity?: string,
|
|
212
|
+
/** The model the run is known to use (a resume: what its first launch said), when known. */
|
|
213
|
+
model?: string): {
|
|
145
214
|
mcpServers: Record<string, unknown>;
|
|
146
215
|
};
|
|
216
|
+
/**
|
|
217
|
+
* A launch's process identity, `<host>:<pid>:<startedAtMs>` (the pod's
|
|
218
|
+
* `X-Synap-Process`). The config is written before `claude` exists, so the pid
|
|
219
|
+
* is the worker's and the time is the launch's: a resume is a new launch and
|
|
220
|
+
* reads as a new process, never a twin of the one that ended.
|
|
221
|
+
*/
|
|
222
|
+
export declare function runProcessIdentity(launchedAtMs: number, hostname?: string, pid?: number): string;
|
|
223
|
+
/**
|
|
224
|
+
* The headers of the worker's calls once a run's process exited: the run's
|
|
225
|
+
* conversation and process, state `ended`, so the pod ends that runner at once
|
|
226
|
+
* rather than when its live window runs out. None for a run that never named
|
|
227
|
+
* its conversation or process.
|
|
228
|
+
*/
|
|
229
|
+
export declare function runEndHeaders(rec: Pick<RunRecord, "claudeSessionId" | "process" | "model">): Record<string, string> | undefined;
|
|
147
230
|
/** Writes the run config owner-only (it holds the key) and returns its path. */
|
|
148
231
|
export declare function writeRunMcpConfig(dir: string, config: {
|
|
149
232
|
mcpServers: Record<string, unknown>;
|
|
@@ -162,6 +245,8 @@ export declare function buildWorkerPrompt(p: {
|
|
|
162
245
|
mcpServer: string;
|
|
163
246
|
/** A design run: the deliverable is a claude.ai Design artifact. */
|
|
164
247
|
design?: boolean;
|
|
248
|
+
/** A code run's own folder: where its worktrees go, so two runs never share a checkout. */
|
|
249
|
+
treeDir?: string;
|
|
165
250
|
}): string;
|
|
166
251
|
/** How long a work-folder run waits for an answer to its ask before it ends `blocked`. */
|
|
167
252
|
export declare const ASK_WAIT_MINUTES = 30;
|
|
@@ -179,10 +264,24 @@ export declare function buildWorkFolderPrompt(p: {
|
|
|
179
264
|
mcpServer: string;
|
|
180
265
|
}): string;
|
|
181
266
|
/**
|
|
182
|
-
*
|
|
183
|
-
*
|
|
267
|
+
* What the allowlist's `CLAUDE_*` must NOT pass: the variables that switch
|
|
268
|
+
* Claude Code off the subscription to another provider (Bedrock, Vertex,
|
|
269
|
+
* Foundry, Bedrock Mantle, Claude Platform on AWS — `CLAUDE_CODE_USE_*`), skip
|
|
270
|
+
* that provider's own auth for a gateway (`CLAUDE_CODE_SKIP_*_AUTH`), or hand
|
|
271
|
+
* routing to a host platform (`CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST`). Any
|
|
272
|
+
* future `CLAUDE_CODE_USE_*` provider is blocked too; the one `USE_` flag that
|
|
273
|
+
* is not a provider (the Windows PowerShell tool) passes. Without this a run
|
|
274
|
+
* would report `anthropic` while another provider served it.
|
|
275
|
+
*/
|
|
276
|
+
export declare const RUN_ENV_BLOCKLIST: RegExp[];
|
|
277
|
+
export declare const RUN_ENV_ALLOWLIST: RegExp[];
|
|
278
|
+
/**
|
|
279
|
+
* The launched environment, built from an ALLOWLIST: a secret in the worker's
|
|
280
|
+
* own environment (its env file holds deploy tokens) never reaches a run's
|
|
281
|
+
* Bash. The subscription, never a stray API key. With an account picked, its
|
|
282
|
+
* token REPLACES any token the worker inherited. GitHub: only the App's.
|
|
184
283
|
*/
|
|
185
|
-
export declare function workerEnv(base: NodeJS.ProcessEnv, claudeConfigDir?: string, oauthToken?: string): NodeJS.ProcessEnv;
|
|
284
|
+
export declare function workerEnv(base: NodeJS.ProcessEnv, claudeConfigDir?: string, oauthToken?: string, github?: GithubRunEnv): NodeJS.ProcessEnv;
|
|
186
285
|
export interface LimitHit {
|
|
187
286
|
error: string;
|
|
188
287
|
/** When the limit lifts (ms), when Claude Code said. */
|
|
@@ -191,6 +290,8 @@ export interface LimitHit {
|
|
|
191
290
|
/** What one run's events say about how it ended. */
|
|
192
291
|
export interface RunEnd {
|
|
193
292
|
claudeSessionId?: string;
|
|
293
|
+
/** The model the stream's `init` names, when it is an id the pod keeps. */
|
|
294
|
+
model?: string;
|
|
194
295
|
/** The `result` text (stream) or the last main assistant text (transcript). */
|
|
195
296
|
text?: string;
|
|
196
297
|
/** Set when the run's LAST main-thread turn was refused by a usage limit. */
|
|
@@ -248,6 +349,15 @@ export declare const CODE_OUTPUT_KINDS: readonly ["pull_request", "branch", "cod
|
|
|
248
349
|
* session THROWS: it is not "a code session".
|
|
249
350
|
*/
|
|
250
351
|
export declare function sessionProtocol(getSessionText: string): WorkerProtocol;
|
|
352
|
+
export declare const WORKER_PROTOCOLS: readonly WorkerProtocol[];
|
|
353
|
+
/**
|
|
354
|
+
* `--protocols`: the protocols a worker takes. Absent = every protocol. An
|
|
355
|
+
* unknown or empty list THROWS: a typo must never quietly widen (or empty)
|
|
356
|
+
* what a computer runs.
|
|
357
|
+
*/
|
|
358
|
+
export declare function parseProtocols(list: string | undefined): ReadonlySet<WorkerProtocol>;
|
|
359
|
+
/** The one room line a session gets when this worker does not take its protocol. */
|
|
360
|
+
export declare function refusedProtocolLine(protocol: WorkerProtocol, takes: ReadonlySet<WorkerProtocol>): string;
|
|
251
361
|
/** A work-folder run's folder: `<root>/<sessionId>`. */
|
|
252
362
|
export declare function workFolderFor(root: string, sessionId: string): string;
|
|
253
363
|
/** The short id `claude --bg` prints ("backgrounded · fd009109"). */
|
|
@@ -275,6 +385,83 @@ export declare const BG_BLOCKED_MAX_MS: number;
|
|
|
275
385
|
export declare const BG_SETTLE_MS: number;
|
|
276
386
|
/** The final `WORKER-RESULT:` line of a run's result text, if present. */
|
|
277
387
|
export declare function workerVerdict(resultText: string | undefined): string | undefined;
|
|
388
|
+
export type VerdictStatus = "done" | "blocked" | "failed";
|
|
389
|
+
/**
|
|
390
|
+
* `done — <sentence>` (a `workerVerdict`) → its status and sentence; anything
|
|
391
|
+
* else is no verdict. The prompt's own template echoed back
|
|
392
|
+
* (`done | blocked | failed — …`) names no status: it is no verdict either.
|
|
393
|
+
*/
|
|
394
|
+
export declare function parseVerdict(verdict: string | undefined): {
|
|
395
|
+
status: VerdictStatus;
|
|
396
|
+
sentence: string;
|
|
397
|
+
} | undefined;
|
|
398
|
+
/**
|
|
399
|
+
* The verdict the session hears, given how the process ended: `done` only from
|
|
400
|
+
* a run that exited cleanly; a run that said `done` and then exited non-zero is
|
|
401
|
+
* `failed`, with what it said kept. `blocked` and `failed` stand as they are.
|
|
402
|
+
*/
|
|
403
|
+
export declare function verdictForExit(v: {
|
|
404
|
+
status: VerdictStatus;
|
|
405
|
+
sentence: string;
|
|
406
|
+
}, exitedOk: boolean, exitCode?: number | null): {
|
|
407
|
+
status: VerdictStatus;
|
|
408
|
+
sentence: string;
|
|
409
|
+
};
|
|
410
|
+
/** The one room line a verdict becomes. */
|
|
411
|
+
export declare function verdictRoomLine(v: {
|
|
412
|
+
status: VerdictStatus;
|
|
413
|
+
sentence: string;
|
|
414
|
+
}): string;
|
|
415
|
+
/**
|
|
416
|
+
* What the room hears from a run that FAILED without a verdict (it crashed,
|
|
417
|
+
* exited non-zero without `WORKER-RESULT:`, sat blocked, vanished, ran out of
|
|
418
|
+
* usage-limit resumes, or the worker restarted under it): how it ended and the
|
|
419
|
+
* reason the record keeps, on one line.
|
|
420
|
+
*/
|
|
421
|
+
export declare function deadRunSentence(rec: Pick<RunRecord, "result" | "exitCode">): string;
|
|
422
|
+
/**
|
|
423
|
+
* At most this many tries to record one run's end on the pod. A retry runs
|
|
424
|
+
* only after the tick's orient read answered, so a pod outage does not use them up.
|
|
425
|
+
*/
|
|
426
|
+
export declare const MAX_SETTLE_ATTEMPTS = 10;
|
|
427
|
+
/**
|
|
428
|
+
* The end a run owes the pod, given what it said and how its process ended:
|
|
429
|
+
* its verdict (`verdictForExit`), or for a run that failed without one a
|
|
430
|
+
* synthesised `failed` (`deadRunSentence`), so a dead run is never silent.
|
|
431
|
+
* `undefined` when there is nothing to tell (no verdict, not failed, and no
|
|
432
|
+
* runner to end).
|
|
433
|
+
*/
|
|
434
|
+
export declare function pendingSettle(rec: RunRecord, verdictLine: string | undefined, exitedOk: boolean): PendingSettle | undefined;
|
|
435
|
+
/**
|
|
436
|
+
* Outcome kinds a verdict never claims: a pull request is delivered only when
|
|
437
|
+
* it merges (rule work-units), and the merge sweeper marks it met then; a
|
|
438
|
+
* branch or a commit is a step toward that PR, not a deliverable of its own.
|
|
439
|
+
*/
|
|
440
|
+
export declare const UNCLAIMABLE_OUTCOME_KINDS: ReadonlySet<string>;
|
|
441
|
+
/** What the session read says a `done` verdict may claim. */
|
|
442
|
+
export type VerdictClaim = {
|
|
443
|
+
kind: "one";
|
|
444
|
+
key: string;
|
|
445
|
+
} | {
|
|
446
|
+
kind: "none";
|
|
447
|
+
} | {
|
|
448
|
+
kind: "several";
|
|
449
|
+
keys: string[];
|
|
450
|
+
}
|
|
451
|
+
/** The outcomes list could not be read: nothing is claimed, and that is said. */
|
|
452
|
+
| {
|
|
453
|
+
kind: "unreadable";
|
|
454
|
+
why: string;
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* The outcome a `done` verdict claims, from the `synap_get_session` read: the
|
|
458
|
+
* session's ONE open deliverable the agent owns (not a fact, not the person's,
|
|
459
|
+
* not met, not withdrawn, never a PR, branch or commit). None, or several, and
|
|
460
|
+
* nothing is claimed: which of several the run finished is the run's to say
|
|
461
|
+
* (it claims each as it records it). An outcomes list that could not be read is
|
|
462
|
+
* `unreadable`, never "none open".
|
|
463
|
+
*/
|
|
464
|
+
export declare function verdictClaim(getSessionText: string): VerdictClaim;
|
|
278
465
|
export interface RoomMessage {
|
|
279
466
|
id?: string;
|
|
280
467
|
content?: string;
|
|
@@ -290,6 +477,17 @@ export declare const MAX_RESUMES = 20;
|
|
|
290
477
|
* run. The worker's own posts carry its agent id and never resume it.
|
|
291
478
|
*/
|
|
292
479
|
export declare function followUpsSince(messages: RoomMessage[], since: string, selfAgentId: string): RoomMessage[];
|
|
480
|
+
/**
|
|
481
|
+
* Whether a session's quiet period began with THIS worker's own run: the pod's
|
|
482
|
+
* `quietSince` is the session's newest activity, and the run's own room posts
|
|
483
|
+
* (its claim, its "Look at first:", the worker's `Result:` line) count. Quiet
|
|
484
|
+
* since then means the run ended and nothing happened after it; resuming it
|
|
485
|
+
* would only re-run it (up to MAX_RESUMES paid runs). Someone else's message
|
|
486
|
+
* after the run resumes it through the follow-up path instead.
|
|
487
|
+
*/
|
|
488
|
+
export declare function quietIsOwnRun(item: Pick<HandedItem, "quietSince">, rec: Pick<RunRecord, "launchedAt">): boolean;
|
|
489
|
+
/** The prompt that resumes a run whose session went quiet and was handed back (`resume`). */
|
|
490
|
+
export declare function buildQuietResumePrompt(quietSince: string | undefined): string;
|
|
293
491
|
/** The prompt that resumes a finished run with the room's new messages. */
|
|
294
492
|
export declare function buildFollowUpPrompt(messages: RoomMessage[]): string;
|
|
295
493
|
export interface ExecResult {
|
|
@@ -313,6 +511,10 @@ export interface WorkerDeps {
|
|
|
313
511
|
fetchImpl?: typeof fetch;
|
|
314
512
|
now?: () => number;
|
|
315
513
|
say?: (line: string) => void;
|
|
514
|
+
/** The App's installation token (tests stub it); default: from the GitHub App file, when there is one. */
|
|
515
|
+
githubToken?: InstallationTokenSource;
|
|
516
|
+
/** `git` for the worktree sweep (tests stub it). */
|
|
517
|
+
git?: GitFn;
|
|
316
518
|
}
|
|
317
519
|
export interface Worker {
|
|
318
520
|
tick(): Promise<{
|