toolroll 0.9.67 → 0.9.68
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/CHANGELOG.md +36 -0
- package/dist/acp-client.js +1 -3
- package/dist/agent-board.d.ts +0 -1
- package/dist/agent-board.js +0 -1
- package/dist/agent-fence.js +3 -0
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +31 -29
- package/dist/builder.js +3 -26
- package/dist/canonical-json.d.ts +14 -0
- package/dist/canonical-json.js +36 -0
- package/dist/chat-actions.js +1 -2
- package/dist/chat-admission.d.ts +19 -0
- package/dist/chat-admission.js +22 -0
- package/dist/chat-channel.js +1 -1
- package/dist/chat-delivery.js +0 -1
- package/dist/chat-follow.d.ts +17 -0
- package/dist/chat-follow.js +32 -0
- package/dist/chat-messages.js +0 -1
- package/dist/chat-settings-page.d.ts +23 -0
- package/dist/chat-settings-page.js +41 -0
- package/dist/claim.js +3 -6
- package/dist/claude-print.d.ts +12 -0
- package/dist/claude-print.js +34 -0
- package/dist/cli-http.d.ts +0 -7
- package/dist/cli-http.js +0 -21
- package/dist/cli.d.ts +3 -10
- package/dist/cli.js +15 -24
- package/dist/coding-provider.js +2 -1
- package/dist/coding-workspace.js +1 -1
- package/dist/{team-cli.d.ts → connect-cli.d.ts} +5 -9
- package/dist/connect-cli.js +331 -0
- package/dist/containment.d.ts +0 -2
- package/dist/containment.js +2 -6
- package/dist/contracts/chat-actions.d.ts +0 -1
- package/dist/contracts/chat-content.d.ts +0 -4
- package/dist/contracts/chat-content.js +0 -2
- package/dist/contracts/chat-tables.d.ts +0 -1
- package/dist/contracts/cli.d.ts +0 -62
- package/dist/contracts/cli.js +0 -4
- package/dist/contracts/coding-workspace.js +1 -1
- package/dist/contracts/contract.js +1 -1
- package/dist/contracts/flow-card.d.ts +0 -1
- package/dist/contracts/flow-propose.d.ts +0 -1
- package/dist/contracts/flow-send.js +1 -1
- package/dist/contracts/gateway-tools.d.ts +0 -1
- package/dist/contracts/integration-metadata.js +1 -1
- package/dist/contracts/lead-tools.d.ts +0 -1
- package/dist/contracts/memory-pass.d.ts +0 -1
- package/dist/contracts/memory-pass.js +1 -1
- package/dist/contracts/plan.js +1 -1
- package/dist/contracts/project-knowledge.js +1 -1
- package/dist/contracts/project-skills.d.ts +0 -1
- package/dist/contracts/proof.js +1 -1
- package/dist/contracts/recipes.js +1 -1
- package/dist/contracts/scope.js +1 -1
- package/dist/contracts/scout-report.js +1 -1
- package/dist/contracts/teams-callback.d.ts +0 -1
- package/dist/converse.d.ts +0 -6
- package/dist/converse.js +0 -19
- package/dist/daemon.js +3 -2
- package/dist/decision.d.ts +1 -3
- package/dist/deprecations.d.ts +12 -4
- package/dist/deprecations.js +27 -4
- package/dist/desktop-update-recovery.js +1 -1
- package/dist/desktop-update.js +3 -2
- package/dist/discord-chat.js +4 -15
- package/dist/discord-settings.d.ts +2 -6
- package/dist/discord-settings.js +10 -38
- package/dist/discord.js +12 -23
- package/dist/evidence.d.ts +0 -1
- package/dist/evidence.js +0 -3
- package/dist/exec.d.ts +0 -9
- package/dist/exec.js +3 -81
- package/dist/flow-actions.js +0 -3
- package/dist/flow-draft.d.ts +1 -3
- package/dist/flow-draft.js +15 -28
- package/dist/flow-scripts.d.ts +1 -8
- package/dist/flow-scripts.js +0 -13
- package/dist/flow-send.js +1 -1
- package/dist/flow-share.d.ts +0 -2
- package/dist/flow-share.js +1 -6
- package/dist/flow-steps.js +1 -1
- package/dist/flow-triggers.d.ts +0 -1
- package/dist/flow-triggers.js +1 -2
- package/dist/flow-unattended.js +1 -12
- package/dist/flows.js +1 -1
- package/dist/health-cli.d.ts +2 -2
- package/dist/health-cli.js +1 -1
- package/dist/invoke.d.ts +0 -3
- package/dist/invoke.js +5 -3
- package/dist/is-record.d.ts +2 -0
- package/dist/is-record.js +2 -0
- package/dist/lead-doors.d.ts +1 -5
- package/dist/lead-doors.js +0 -4
- package/dist/lead-tools.js +3 -6
- package/dist/live-detail.d.ts +0 -1
- package/dist/live-detail.js +0 -6
- package/dist/live-terminal.js +2 -1
- package/dist/live.d.ts +0 -1
- package/dist/live.js +1 -2
- package/dist/mcp-core.js +0 -2
- package/dist/memory-pass.js +1 -1
- package/dist/model-catalog.js +1 -1
- package/dist/modes.js +1 -12
- package/dist/operate.js +383 -750
- package/dist/password-bearer.d.ts +0 -2
- package/dist/password-bearer.js +0 -4
- package/dist/peek-cli.js +2 -1
- package/dist/peek.d.ts +2 -26
- package/dist/peek.js +2 -168
- package/dist/phase-routing.d.ts +0 -4
- package/dist/phase-routing.js +2 -21
- package/dist/planner-source.d.ts +2 -1
- package/dist/planner-source.js +2 -12
- package/dist/planner.js +2 -14
- package/dist/process-tree.d.ts +0 -2
- package/dist/process-tree.js +0 -10
- package/dist/project-delete.d.ts +0 -2
- package/dist/project-delete.js +1 -3
- package/dist/project-learning.d.ts +0 -14
- package/dist/project-skills.d.ts +0 -1
- package/dist/project-skills.js +0 -14
- package/dist/project-tools.js +2 -10
- package/dist/provider.d.ts +0 -17
- package/dist/provider.js +0 -26
- package/dist/pulls.d.ts +0 -2
- package/dist/pulls.js +0 -9
- package/dist/release-adapters.d.ts +3 -0
- package/dist/release-adapters.js +15 -5
- package/dist/remote-exec.d.ts +3 -2
- package/dist/remote-exec.js +3 -2
- package/dist/repos.d.ts +0 -5
- package/dist/repos.js +2 -10
- package/dist/repository-context.d.ts +0 -2
- package/dist/repository-context.js +0 -4
- package/dist/restart-certification.d.ts +0 -2
- package/dist/restart-certification.js +1 -5
- package/dist/result-follow-ups.d.ts +1 -4
- package/dist/result-follow-ups.js +1 -6
- package/dist/result-review.d.ts +0 -2
- package/dist/result-review.js +0 -2
- package/dist/retention.d.ts +0 -1
- package/dist/retention.js +0 -1
- package/dist/review-context.d.ts +0 -1
- package/dist/review-context.js +1 -12
- package/dist/reviewer.d.ts +0 -16
- package/dist/reviewer.js +0 -18
- package/dist/run-closeout.js +2 -1
- package/dist/scope.js +1 -13
- package/dist/scout-browser.js +2 -1
- package/dist/serve.js +2 -2
- package/dist/server/console.css +5 -133
- package/dist/server/flows.js +2 -2
- package/dist/server/http.d.ts +2 -0
- package/dist/server/http.js +5 -0
- package/dist/server/pages.js +2 -2
- package/dist/server/people-tokens.js +3 -4
- package/dist/server/remote.js +1 -6
- package/dist/server/render-chat.d.ts +9 -2
- package/dist/server/render-chat.js +53 -7
- package/dist/server/render-results.d.ts +0 -6
- package/dist/server/render-results.js +3 -9
- package/dist/server/render-tasks.js +4 -5
- package/dist/server/route-table.d.ts +0 -4
- package/dist/server/route-table.js +0 -6
- package/dist/server/settings.js +23 -23
- package/dist/server/task-views.js +1 -1
- package/dist/server/tasks.js +3 -3
- package/dist/setup-guide.d.ts +2 -2
- package/dist/skills.d.ts +0 -6
- package/dist/skills.js +0 -8
- package/dist/slack-chat.d.ts +1 -7
- package/dist/slack-chat.js +5 -18
- package/dist/slack-settings.d.ts +2 -6
- package/dist/slack-settings.js +10 -38
- package/dist/slack.js +12 -23
- package/dist/sleep.d.ts +2 -0
- package/dist/sleep.js +2 -0
- package/dist/store.d.ts +0 -73
- package/dist/store.js +3 -177
- package/dist/structured-output.d.ts +0 -1
- package/dist/structured-output.js +0 -1
- package/dist/subagent-work.d.ts +0 -2
- package/dist/subagent-work.js +0 -2
- package/dist/subagents.d.ts +1 -3
- package/dist/subagents.js +20 -33
- package/dist/subscription-chat.d.ts +1 -3
- package/dist/subscription-chat.js +2 -3
- package/dist/task-sizing.d.ts +1 -4
- package/dist/task-sizing.js +3 -10
- package/dist/teams-chat.js +4 -11
- package/dist/teams.js +12 -19
- package/dist/telegram-lead.js +2 -2
- package/dist/telegram.js +1 -1
- package/dist/token-notices.d.ts +0 -11
- package/dist/token-notices.js +1 -5
- package/dist/tool-secret-store.d.ts +0 -3
- package/dist/tool-secret-store.js +2 -11
- package/dist/toolroll-update.d.ts +0 -2
- package/dist/toolroll-update.js +2 -2
- package/dist/verification-evidence.d.ts +0 -1
- package/dist/verification-evidence.js +0 -1
- package/dist/webhooks.d.ts +0 -1
- package/dist/webhooks.js +0 -1
- package/dist/workspace-ui.d.ts +0 -8
- package/dist/workspace-ui.js +1 -6
- package/package.json +1 -1
- package/dist/contracts/native-census.d.ts +0 -42
- package/dist/contracts/native-census.js +0 -36
- package/dist/process-recovery-anchor.d.ts +0 -75
- package/dist/process-recovery-anchor.js +0 -188
- package/dist/process-recovery-coalition.d.ts +0 -25
- package/dist/process-recovery-coalition.js +0 -105
- package/dist/process-recovery-eligibility.d.ts +0 -179
- package/dist/process-recovery-eligibility.js +0 -174
- package/dist/process-recovery-native.d.ts +0 -161
- package/dist/process-recovery-native.js +0 -595
- package/dist/process-recovery-provenance.d.ts +0 -163
- package/dist/process-recovery-provenance.js +0 -292
- package/dist/process-recovery-services.d.ts +0 -56
- package/dist/process-recovery-services.js +0 -191
- package/dist/process-recovery-settlement.d.ts +0 -32
- package/dist/process-recovery-settlement.js +0 -133
- package/dist/process-recovery.d.ts +0 -41
- package/dist/process-recovery.js +0 -91
- package/dist/session-brief.d.ts +0 -8
- package/dist/session-brief.js +0 -24
- package/dist/session-cli.d.ts +0 -42
- package/dist/session-cli.js +0 -271
- package/dist/session-contract.d.ts +0 -183
- package/dist/session-contract.js +0 -122
- package/dist/session-remote.d.ts +0 -19
- package/dist/session-remote.js +0 -86
- package/dist/session-service.d.ts +0 -22
- package/dist/session-service.js +0 -157
- package/dist/team-cli.js +0 -692
package/dist/builder.js
CHANGED
|
@@ -74,7 +74,7 @@ import { captureParkEvidence, captureTerminalDiff, captureBaseTree, evidenceRoot
|
|
|
74
74
|
import { attemptKeyOf, closeRunQuestions, noteOpenTool, recordAgentTurn } from "./agent-board.js";
|
|
75
75
|
import { openStatusFile, pulseAgentBoard, removeStatusFile, statusBrief } from "./agent-pulse.js";
|
|
76
76
|
import { withAgentTurns } from "./agent-turns.js";
|
|
77
|
-
import { PROOF_LIMITS, parseProof, serializeProof, adjudicate,
|
|
77
|
+
import { PROOF_LIMITS, parseProof, serializeProof, adjudicate, sameDiffStatFacts } from "./proof.js";
|
|
78
78
|
import { parseHandoffArtifact } from "./contracts/handoff.js";
|
|
79
79
|
import { captureReviewContext } from "./review-context.js";
|
|
80
80
|
import { authoritySnapshotDigest, classifyRevisionAuthority, isMilestoneRegression, milestonesOf, parseExecutionPlanDocument, parsePlanRevisionProposal, parseProgressSnapshot, renderExecutionPlanDocument, PROGRESS_LIMITS, REVISION_LIMITS, } from "./plan.js";
|
|
@@ -369,7 +369,7 @@ export function proveApprovedProfile(scope, given) {
|
|
|
369
369
|
* that forgot one is exactly the caller this is protecting against.
|
|
370
370
|
*/
|
|
371
371
|
export async function build(store, request) {
|
|
372
|
-
const { taskId, taskRef, runner, worktree, branch, now, permissionMode = "auto", skipPermissions = false, provider = "claude",
|
|
372
|
+
const { taskId, taskRef, runner, worktree, branch, now, permissionMode = "auto", skipPermissions = false, provider = "claude", maxTurns = DEFAULT_MAX_TURNS, timeoutMs = DEFAULT_BUILD_TIMEOUT_MS, agent, git: givenGit = run, } = request;
|
|
373
373
|
// The plane's own git on this build's checkout runs hardened, and never where the checkout's config names a command.
|
|
374
374
|
const git = buildCheckoutGit(givenGit, worktree, store.getWorktree(worktree)?.repo ?? null);
|
|
375
375
|
const scope = store.getScope(taskId);
|
|
@@ -1527,7 +1527,7 @@ export async function settleProviderOutcome(captured, result) {
|
|
|
1527
1527
|
}
|
|
1528
1528
|
catch { /* The commit gate still excludes it. */ }
|
|
1529
1529
|
}
|
|
1530
|
-
const { store, request, agent, git, worktree, branch, baseRevision, taskId, taskRef, runner, provider, scope, effective, answers, timeoutMs, root, mailbox, done,
|
|
1530
|
+
const { store, request, agent, git, worktree, branch, baseRevision, taskId, taskRef, runner, provider, scope, effective, answers, timeoutMs, root, mailbox, done, clock } = captured;
|
|
1531
1531
|
// THE STOP FENCE after the provider (v52): a stop recorded while the
|
|
1532
1532
|
// agent ran ends the attempt HERE, before any handoff is read, any park
|
|
1533
1533
|
// sealed, or any commit made — whatever the process wrote is preserved
|
|
@@ -3534,29 +3534,6 @@ async function stripEvidenceImages(git, worktree) {
|
|
|
3534
3534
|
const rest = await git(GIT, ["--no-optional-locks", "diff", "--cached", "--quiet"], { cwd: worktree });
|
|
3535
3535
|
return { ok: true, notice: evidenceImagesNotice(images), staged: rest.code !== 0 };
|
|
3536
3536
|
}
|
|
3537
|
-
/**
|
|
3538
|
-
* `claude --output-format json` returns an envelope: the result is the
|
|
3539
|
-
* summary, and the session id is what lets a malformed park be repaired by
|
|
3540
|
-
* resuming the conversation that produced it instead of paying for a new one.
|
|
3541
|
-
*/
|
|
3542
|
-
function envelope(stdout) {
|
|
3543
|
-
try {
|
|
3544
|
-
const parsed = JSON.parse(stdout);
|
|
3545
|
-
return {
|
|
3546
|
-
summary: typeof parsed.result === "string" && parsed.result.trim() !== ""
|
|
3547
|
-
? parsed.result.trim()
|
|
3548
|
-
: "unattended build",
|
|
3549
|
-
...(typeof parsed.session_id === "string" && parsed.session_id !== ""
|
|
3550
|
-
? { sessionId: parsed.session_id }
|
|
3551
|
-
: {}),
|
|
3552
|
-
};
|
|
3553
|
-
}
|
|
3554
|
-
catch {
|
|
3555
|
-
// An agent that printed something unparseable still did work — and a
|
|
3556
|
-
// session nobody can name simply cannot be resumed.
|
|
3557
|
-
return { summary: "unattended build" };
|
|
3558
|
-
}
|
|
3559
|
-
}
|
|
3560
3537
|
/**
|
|
3561
3538
|
* What a non-zero agent exit means, in words a person can act on: the
|
|
3562
3539
|
* harness's own result line names the ending (a turn ceiling, an error
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic JSON for digests and equality. These bytes are sealed into approvals and stored digests, so neither
|
|
3
|
+
* function may change its output for any input. The two differ on purpose and must stay distinct:
|
|
4
|
+
*
|
|
5
|
+
* - `canonicalJson` drops object keys whose value is undefined and returns JSON.stringify's own answer for a bare
|
|
6
|
+
* value (an undefined array element therefore renders as nothing: `[,1]`).
|
|
7
|
+
* - `canonicalJsonUndefinedAsNull` keeps every key and renders an undefined (or otherwise unserializable) value as
|
|
8
|
+
* `null` — the planner contract's bytes.
|
|
9
|
+
*
|
|
10
|
+
* Both sort object keys by UTF-16 code units and keep arrays in order.
|
|
11
|
+
*/
|
|
12
|
+
export declare function canonicalJson(value: unknown): string;
|
|
13
|
+
/** Stable JSON for hashing and equality only — no filling, no trimming; undefined reads as null. */
|
|
14
|
+
export declare function canonicalJsonUndefinedAsNull(value: unknown): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic JSON for digests and equality. These bytes are sealed into approvals and stored digests, so neither
|
|
3
|
+
* function may change its output for any input. The two differ on purpose and must stay distinct:
|
|
4
|
+
*
|
|
5
|
+
* - `canonicalJson` drops object keys whose value is undefined and returns JSON.stringify's own answer for a bare
|
|
6
|
+
* value (an undefined array element therefore renders as nothing: `[,1]`).
|
|
7
|
+
* - `canonicalJsonUndefinedAsNull` keeps every key and renders an undefined (or otherwise unserializable) value as
|
|
8
|
+
* `null` — the planner contract's bytes.
|
|
9
|
+
*
|
|
10
|
+
* Both sort object keys by UTF-16 code units and keep arrays in order.
|
|
11
|
+
*/
|
|
12
|
+
export function canonicalJson(value) {
|
|
13
|
+
if (Array.isArray(value))
|
|
14
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
15
|
+
if (value !== null && typeof value === "object") {
|
|
16
|
+
const entries = Object.entries(value)
|
|
17
|
+
.filter(([, v]) => v !== undefined)
|
|
18
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
19
|
+
.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`);
|
|
20
|
+
return `{${entries.join(",")}}`;
|
|
21
|
+
}
|
|
22
|
+
return JSON.stringify(value);
|
|
23
|
+
}
|
|
24
|
+
/** Stable JSON for hashing and equality only — no filling, no trimming; undefined reads as null. */
|
|
25
|
+
export function canonicalJsonUndefinedAsNull(value) {
|
|
26
|
+
if (Array.isArray(value))
|
|
27
|
+
return `[${value.map(canonicalJsonUndefinedAsNull).join(",")}]`;
|
|
28
|
+
if (value !== null && typeof value === "object") {
|
|
29
|
+
const body = value;
|
|
30
|
+
return `{${Object.keys(body)
|
|
31
|
+
.sort()
|
|
32
|
+
.map(key => `${JSON.stringify(key)}:${canonicalJsonUndefinedAsNull(body[key])}`)
|
|
33
|
+
.join(",")}}`;
|
|
34
|
+
}
|
|
35
|
+
return JSON.stringify(value) ?? "null";
|
|
36
|
+
}
|
package/dist/chat-actions.js
CHANGED
|
@@ -16,7 +16,6 @@ import { changeKnowledge, knowledgeVersion, knowledgeView, } from "./project-kno
|
|
|
16
16
|
import { passwordlessSessionFor, approve, authenticateApprover, describeScope, scopeAuthorityOf, freshnessFor, spendingOnlyOnSuccess, ssoNowAvailable, withFreshness, } from "./scope.js";
|
|
17
17
|
import { readVerifiedArtifact, scanForSecrets } from "./evidence.js";
|
|
18
18
|
import { readAuthModeStrict } from "./keys.js";
|
|
19
|
-
import { parseProof } from "./proof.js";
|
|
20
19
|
import { acceptAndCompleteAsOperator, assignmentOf } from "./assignment.js";
|
|
21
20
|
import { getDecision, recordDecision, retireDecision } from "./project-memory.js";
|
|
22
21
|
import { resumeTaskStop, taskControlOf } from "./task-control.js";
|
|
@@ -26,7 +25,7 @@ import { saveScript, scriptDigest, validateScript } from "./flow-scripts.js";
|
|
|
26
25
|
import { addCardToFlow, advanceFlows, cancelFlowCard, crossProjectProblem, decideFlowCard, flowCardHref, flowCardText, flowDefinitionOf, moveCardInFlow } from "./flow-engine.js";
|
|
27
26
|
import { chooseFlowCard, flowPersonOf } from "./flow-send.js";
|
|
28
27
|
import { assignFlowCard, commentOnFlowCard, flowPeople, mentionsIn, watchFlowCard } from "./flow-people.js";
|
|
29
|
-
import { addFlowTriggerTo, describeTrigger, readLinearKey, removeFlowTrigger,
|
|
28
|
+
import { addFlowTriggerTo, describeTrigger, readLinearKey, removeFlowTrigger, takesDeliveries, triggerConfigOf, validateTriggerConfig } from "./flow-triggers.js";
|
|
30
29
|
import { describeSchedule, parseSchedule } from "./flow-schedule.js";
|
|
31
30
|
import { dirname } from "node:path";
|
|
32
31
|
import { handleOf, parseSoul, SOUL_CHARS, SUBAGENT_TEMPLATES, subagentLabel } from "./subagents.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ChatBinding, ChatState } from "./chat-delivery-state.js";
|
|
2
|
+
import type { ChatEventBody } from "./contracts/chat-content.js";
|
|
3
|
+
import type { ChatApp } from "./contracts/flow.js";
|
|
4
|
+
/**
|
|
5
|
+
* Whether a received message or tap, already proved to come from this app, may be queued — and under which binding.
|
|
6
|
+
* A room (any conversation that isn't a direct message) is taken only while it follows a conversation, for the words
|
|
7
|
+
* that make it follow one, or when it feeds a flow (v89: anyone's message then, as a card with no say over anything).
|
|
8
|
+
* A pairing message comes only from someone unpaired, in a direct message; everything else only from a live paired
|
|
9
|
+
* member in their own conversation or a followed room. Null when it is not taken.
|
|
10
|
+
*/
|
|
11
|
+
export declare function admitChatEvent(state: ChatState, app: ChatApp, installation: string, event: {
|
|
12
|
+
kind: "message" | "pair" | "action";
|
|
13
|
+
member: string;
|
|
14
|
+
channel: string;
|
|
15
|
+
isRoom: boolean;
|
|
16
|
+
payload: ChatEventBody;
|
|
17
|
+
}): {
|
|
18
|
+
binding: ChatBinding | null;
|
|
19
|
+
} | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { channelInbox } from "./chat-inbox.js";
|
|
2
|
+
import { roomCommand } from "./chat-rooms.js";
|
|
3
|
+
/**
|
|
4
|
+
* Whether a received message or tap, already proved to come from this app, may be queued — and under which binding.
|
|
5
|
+
* A room (any conversation that isn't a direct message) is taken only while it follows a conversation, for the words
|
|
6
|
+
* that make it follow one, or when it feeds a flow (v89: anyone's message then, as a card with no say over anything).
|
|
7
|
+
* A pairing message comes only from someone unpaired, in a direct message; everything else only from a live paired
|
|
8
|
+
* member in their own conversation or a followed room. Null when it is not taken.
|
|
9
|
+
*/
|
|
10
|
+
export function admitChatEvent(state, app, installation, event) {
|
|
11
|
+
const { kind, member, channel, isRoom, payload } = event;
|
|
12
|
+
const words = "text" in payload ? payload.text ?? "" : "";
|
|
13
|
+
const inbox = isRoom && kind === "message" ? channelInbox(state.store, app, installation, channel, words) : { watched: false, command: false };
|
|
14
|
+
const roomish = isRoom && (state.room(installation, channel) !== null || (kind === "message" && roomCommand(words) !== null) || inbox.watched || inbox.command);
|
|
15
|
+
if (isRoom && !roomish)
|
|
16
|
+
return null;
|
|
17
|
+
const binding = state.bindingFor(installation, member);
|
|
18
|
+
const open = inbox.watched && !inbox.command;
|
|
19
|
+
if (kind === "pair" ? !!binding || isRoom : !open && (!binding || !state.live(binding) || (binding.channel !== channel && !roomish)))
|
|
20
|
+
return null;
|
|
21
|
+
return { binding };
|
|
22
|
+
}
|
package/dist/chat-channel.js
CHANGED
|
@@ -9,7 +9,7 @@ import { LEAD_TOOL_SCHEMAS } from "./lead-tools.js";
|
|
|
9
9
|
import { verifyApproverStanding } from "./principal.js";
|
|
10
10
|
import { canonicalProject } from "./project.js";
|
|
11
11
|
import { phoneText, projectLabel } from "./telegram-status.js";
|
|
12
|
-
import { CHAT_CONTROLS, chatControlHref,
|
|
12
|
+
import { CHAT_CONTROLS, chatControlHref, isChatControl, } from "./chat-controls.js";
|
|
13
13
|
import { CHAT_TASK_ACTIONS, isChatTaskAction } from "./chat-task-actions.js";
|
|
14
14
|
export function channelRepos(store, approver, registry) {
|
|
15
15
|
const seen = new Set();
|
package/dist/chat-delivery.js
CHANGED
|
@@ -23,7 +23,6 @@ import { phoneText, PHONE_HELP, phoneCommand, phoneStatus, phoneTaskView, phoneT
|
|
|
23
23
|
import { applyRoomInbound, conversationRow, roomCardApprover, roomCommand, roomGrantAllowed, roomMessagesAfter, roomMessageText, teamDomain } from "./chat-rooms.js";
|
|
24
24
|
import { isTelegramProgressNotification, proposalTaskOf } from "./store.js";
|
|
25
25
|
import { isShotsKind, resultShotsFor } from "./result-shots.js";
|
|
26
|
-
import { CHAT_APP_NAMES } from "./flow-triggers.js";
|
|
27
26
|
import { answersPrompt, applyDecideFeedback, applyDecideTap, decideFallbackLink, decideOffer, decideTargetOf, hasLiveDecideTokens, linksFor, mergedText, mintDecideButtons, offerFingerprint, openPromptFor, recordChatMerge } from "./chat-decide.js";
|
|
28
27
|
import { mergePullRequest } from "./pull-request-flow.js";
|
|
29
28
|
import { attentionSubjectOf, neverHeld, watchedPing } from "./agent-board.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** A pause that ends early, and quietly, when `signal` aborts. */
|
|
2
|
+
export declare const abortablePause: (signal: AbortSignal) => (ms: number) => Promise<void>;
|
|
3
|
+
/**
|
|
4
|
+
* Works the queue while this connection stays the current, leased one: each pass takes in saved events, plans room
|
|
5
|
+
* messages, delivers one part and plans notifications (when they are on), checking it is still current between steps.
|
|
6
|
+
* A pass waits while the connection isn't ready (offline, or the provider asked to retry later).
|
|
7
|
+
*/
|
|
8
|
+
export declare function workChatQueue(steps: {
|
|
9
|
+
same: () => boolean;
|
|
10
|
+
ready: () => boolean;
|
|
11
|
+
pause: (ms: number) => Promise<void>;
|
|
12
|
+
notifications: () => boolean;
|
|
13
|
+
process: () => Promise<unknown>;
|
|
14
|
+
rooms: () => Promise<unknown>;
|
|
15
|
+
deliver: () => Promise<unknown>;
|
|
16
|
+
notify: () => Promise<unknown>;
|
|
17
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** The loop every hosted chat connection (Slack, Discord, Teams) runs over its saved queue. */
|
|
2
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
3
|
+
/** A pause that ends early, and quietly, when `signal` aborts. */
|
|
4
|
+
export const abortablePause = (signal) => async (ms) => {
|
|
5
|
+
try {
|
|
6
|
+
await sleep(ms, undefined, { signal });
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
/* shutdown */
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Works the queue while this connection stays the current, leased one: each pass takes in saved events, plans room
|
|
14
|
+
* messages, delivers one part and plans notifications (when they are on), checking it is still current between steps.
|
|
15
|
+
* A pass waits while the connection isn't ready (offline, or the provider asked to retry later).
|
|
16
|
+
*/
|
|
17
|
+
export async function workChatQueue(steps) {
|
|
18
|
+
while (steps.same()) {
|
|
19
|
+
if (!steps.ready()) {
|
|
20
|
+
await steps.pause(1000);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
await steps.process();
|
|
24
|
+
if (steps.same())
|
|
25
|
+
await steps.rooms();
|
|
26
|
+
if (steps.same())
|
|
27
|
+
await steps.deliver();
|
|
28
|
+
if (steps.same() && steps.notifications())
|
|
29
|
+
await steps.notify();
|
|
30
|
+
await steps.pause(1000);
|
|
31
|
+
}
|
|
32
|
+
}
|
package/dist/chat-messages.js
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Imports the store's types only (no runtime cycle): the store builds its chats on it.
|
|
10
10
|
*/
|
|
11
|
-
import { join } from "node:path";
|
|
12
11
|
import { neverHeld, watchedPing } from "./agent-board.js";
|
|
13
12
|
import { ChatCore } from "./chat-core.js";
|
|
14
13
|
import { readChatMessageBody, readChatPart, savedChatEventBody, savedChatPart } from "./contracts/chat-content.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Store } from "./store.js";
|
|
2
|
+
import { type Html } from "./html.js";
|
|
3
|
+
import { type ChatIdentity } from "./chat-delivery-state.js";
|
|
4
|
+
export type ChatSettingsOptions = {
|
|
5
|
+
code?: string;
|
|
6
|
+
problem?: string;
|
|
7
|
+
now?: Date;
|
|
8
|
+
who?: string;
|
|
9
|
+
};
|
|
10
|
+
/** What differs between the Slack and Discord settings pages; everything else is one page. */
|
|
11
|
+
export type ChatSettingsPage = {
|
|
12
|
+
provider: "slack" | "discord";
|
|
13
|
+
label: string;
|
|
14
|
+
credentials: ChatIdentity | null;
|
|
15
|
+
/** How to create the app, shown before it is connected. */
|
|
16
|
+
setup: Html;
|
|
17
|
+
/** The connect form's fields around the password. */
|
|
18
|
+
connect: (password: Html) => Html;
|
|
19
|
+
/** The "Access and setup" paragraphs. */
|
|
20
|
+
access: Html;
|
|
21
|
+
};
|
|
22
|
+
/** Settings → Slack or Discord: connect the app once, then each teammate pairs their own account. */
|
|
23
|
+
export declare function chatSettingsHtml(store: Store, dir: string, options: ChatSettingsOptions, page: ChatSettingsPage): Html;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { loadPrimary } from "./webhooks.js";
|
|
2
|
+
import { html, postForm } from "./html.js";
|
|
3
|
+
import { ChatState } from "./chat-delivery-state.js";
|
|
4
|
+
/** Settings → Slack or Discord: connect the app once, then each teammate pairs their own account. */
|
|
5
|
+
export function chatSettingsHtml(store, dir, options, page) {
|
|
6
|
+
const { provider, label, credentials } = page, state = new ChatState(store, provider), bindings = credentials ? state.bindings(credentials.installation).filter(one => state.live(one)) : [], binding = options.who === undefined ? null : bindings.find(one => one.approver === options.who) ?? null, others = bindings.length - (binding === null ? 0 : 1);
|
|
7
|
+
const runtime = credentials ? state.runtime(credentials.installation) : null;
|
|
8
|
+
const now = options.now ?? new Date(), live = runtime &&
|
|
9
|
+
runtime.leaseUntil !== null &&
|
|
10
|
+
runtime.leaseUntil > now.toISOString() &&
|
|
11
|
+
runtime.connected;
|
|
12
|
+
const password = html `<label>Your Toolroll password<input style="min-height:44px" type="password" name="password" autocomplete="current-password" required></label>`;
|
|
13
|
+
const post = (action, content) => postForm(`/settings/${provider}/${action}`, content, { attrs: { class: "card" } });
|
|
14
|
+
const parts = [];
|
|
15
|
+
if (!credentials) {
|
|
16
|
+
parts.push(html `<p>Manage projects and review results in a private ${label} conversation.</p>`, page.setup, post("connect", page.connect(password)));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
parts.push(html `<p><strong>${credentials.workspace}</strong> · ${live ? "Connected" : "Waiting for connection"}</p>`);
|
|
20
|
+
if (runtime?.problem)
|
|
21
|
+
parts.push(html `<p role="status">${String(runtime.problem)}</p>`);
|
|
22
|
+
if (!live && !runtime?.problem)
|
|
23
|
+
parts.push(html `<p>Keep the Toolroll worker running to receive ${label} messages.</p>`);
|
|
24
|
+
if (options.code) {
|
|
25
|
+
parts.push(html `<h2>Pair your account</h2><p>Send this in a direct message to your Toolroll ${label} app. It expires in 10 minutes.</p>`, html `<label>Pairing message<input type="text" readonly value="pair ${options.code}" autocomplete="off" style="width:100%;max-width:100%;font-size:14px;min-height:44px"></label>`, html `<a class="button-link" style="min-height:44px;white-space:nowrap" href="/settings/${provider}">Check connection</a>`);
|
|
26
|
+
}
|
|
27
|
+
else if (binding && state.live(binding)) {
|
|
28
|
+
const { pending, dropped: failed } = state.partCounts(binding.id);
|
|
29
|
+
parts.push(html `<p>Your ${label} account is paired.${others ? ` ${others} teammate${others === 1 ? " is" : "s are"} paired too.` : ""}${pending ? ` ${pending} replies waiting to send.` : ""}${failed ? ` ${failed} replies could not be delivered. Open the saved chat to recover them.` : ""}</p>`, html `<p>Try “What needs my attention?” or “Show the evidence for the latest result.”</p>`, html `<p><a href="/chat">Open saved chat</a></p>`, loadPrimary(process.env, dir) === provider
|
|
30
|
+
? html `<p>Task updates are sent here.</p>`
|
|
31
|
+
: post("alerts", html `<button type="submit">Send task updates here</button>`));
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
parts.push(post("pair", html `<h2>Pair your account</h2><p>Your paired ${label} account can read your connected projects and confirm proposed changes. Password approvals still open in Toolroll.${others ? ` ${others} teammate${others === 1 ? " is" : "s are"} already paired.` : ""}</p>${password}<button type="submit">Create pairing code</button>`));
|
|
35
|
+
}
|
|
36
|
+
if (binding && state.live(binding) && !options.code)
|
|
37
|
+
parts.push(post("unpair", html `${password}<p class="meta">Unpairing ends every open button in your chat. Teammates are unaffected.</p><button type="submit">Unpair my account</button>`));
|
|
38
|
+
parts.push(html `<details><summary>Connection settings</summary>${post("disconnect", html `${password}<button type="submit">Disconnect ${label}</button>`)}</details>`);
|
|
39
|
+
}
|
|
40
|
+
return html `<section style="max-width:42rem;overflow-wrap:anywhere"><p><a href="/settings">Settings</a></p><h1>${label}</h1>${options.problem ? html `<p class="problem" role="alert">${options.problem}</p>` : ""}${parts}<details><summary>Access and setup</summary>${page.access}</details></section>`;
|
|
41
|
+
}
|
package/dist/claim.js
CHANGED
|
@@ -749,7 +749,7 @@ export const MAX_STRIKES = 3;
|
|
|
749
749
|
function sealInterrupted(store, leaseId, args) {
|
|
750
750
|
const { runId, taskId, stopRun, now } = args;
|
|
751
751
|
const db = store.handle;
|
|
752
|
-
|
|
752
|
+
openAttempt(store, leaseId, runId, "an interruption");
|
|
753
753
|
// A lease the world moved past still ends its run as interrupted — that IS the honest ending — touching no task state.
|
|
754
754
|
const fenced = !releaseAs(store, leaseId, "interrupted", now);
|
|
755
755
|
if (args.message !== undefined && args.message.trim() !== "") {
|
|
@@ -797,7 +797,7 @@ function sealInterrupted(store, leaseId, args) {
|
|
|
797
797
|
function sealHandBack(store, leaseId, args) {
|
|
798
798
|
const { runId, taskId, now } = args;
|
|
799
799
|
const db = store.handle;
|
|
800
|
-
|
|
800
|
+
openAttempt(store, leaseId, runId, "a hand-back");
|
|
801
801
|
// A lease the world moved past still ends its run as interrupted — that IS the honest ending — touching no task state.
|
|
802
802
|
const fenced = !releaseAs(store, leaseId, "interrupted", now);
|
|
803
803
|
if (args.message.trim() !== "")
|
|
@@ -974,7 +974,6 @@ const PLAN_BACKOFF_MS = [60_000, 2 * 60_000, 4 * 60_000];
|
|
|
974
974
|
*/
|
|
975
975
|
function sealPlan(store, leaseId, args) {
|
|
976
976
|
const { runId, taskId, plan, now } = args;
|
|
977
|
-
const db = store.handle;
|
|
978
977
|
// The ingestion record (contract handoff, task 1) is composed here from
|
|
979
978
|
// the filed terms and the drafted plan; its file and row land inside the
|
|
980
979
|
// transaction below, only after the stale-source check admits the draft.
|
|
@@ -1199,7 +1198,7 @@ function describeSourceDrift(source, current) {
|
|
|
1199
1198
|
*/
|
|
1200
1199
|
function sealRevision(store, leaseId, args) {
|
|
1201
1200
|
const { runId, taskId, taskRef, revision, now } = args;
|
|
1202
|
-
|
|
1201
|
+
openAttempt(store, leaseId, runId, "a plan revision", "builder");
|
|
1203
1202
|
const stopped = interruptIfStopped(store, { leaseId, runId, taskId, now });
|
|
1204
1203
|
if (stopped !== null)
|
|
1205
1204
|
return { ok: false, reason: "stopped" };
|
|
@@ -1249,7 +1248,6 @@ function sealRevision(store, leaseId, args) {
|
|
|
1249
1248
|
*/
|
|
1250
1249
|
function sealPlanFailure(store, leaseId, args) {
|
|
1251
1250
|
const { runId, taskId, failure: kind, message, now } = args;
|
|
1252
|
-
const db = store.handle;
|
|
1253
1251
|
const run = openAttempt(store, leaseId, runId, "a failure", "planner");
|
|
1254
1252
|
const stopped = interruptIfStopped(store, { leaseId, runId, taskId, now });
|
|
1255
1253
|
if (stopped !== null)
|
|
@@ -1331,7 +1329,6 @@ function sealPlanFailure(store, leaseId, args) {
|
|
|
1331
1329
|
*/
|
|
1332
1330
|
function sealScout(store, leaseId, args) {
|
|
1333
1331
|
const { runId, taskId, report, now } = args;
|
|
1334
|
-
const db = store.handle;
|
|
1335
1332
|
const run = openAttempt(store, leaseId, runId, "a report", "scout");
|
|
1336
1333
|
const stopped = interruptIfStopped(store, { leaseId, runId, taskId, now });
|
|
1337
1334
|
if (stopped !== null)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ExecResult, run } from "./exec.js";
|
|
2
|
+
export type CommandRunner = (file: string, args: readonly string[], options: Parameters<typeof run>[2]) => Promise<ExecResult>;
|
|
3
|
+
/** No tools, no permission prompts, no MCP servers, nothing kept: a `claude -p` call that can only answer. */
|
|
4
|
+
export declare const CLAUDE_NO_TOOLS_ARGS: readonly string[];
|
|
5
|
+
/** `--model <model>`, or nothing for the CLI's own default. */
|
|
6
|
+
export declare const claudeModelArgs: (model: string) => string[];
|
|
7
|
+
/** What a failed call's output says when this computer's Claude isn't signed in. */
|
|
8
|
+
export declare const CLAUDE_SIGNED_OUT: RegExp;
|
|
9
|
+
/** `claude -p --output-format json` stdout as its one object, or null. */
|
|
10
|
+
export declare function claudeJsonBody(stdout: string, cap: number): Record<string, unknown> | null;
|
|
11
|
+
/** Runs `work` in a fresh temporary folder named `<prefix>…`, removed afterwards whatever happens. */
|
|
12
|
+
export declare function inScratchDir<T>(prefix: string, work: (dir: string) => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parts every bare `claude -p` call shares: a throwaway working folder, the flags that leave it with no tools,
|
|
3
|
+
* no MCP servers and no saved session, and reading its one JSON reply. Each caller still builds its own argv around
|
|
4
|
+
* these, in its own order.
|
|
5
|
+
*/
|
|
6
|
+
import { Buffer } from "node:buffer";
|
|
7
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { strictJsonParse } from "./converse.js";
|
|
11
|
+
/** No tools, no permission prompts, no MCP servers, nothing kept: a `claude -p` call that can only answer. */
|
|
12
|
+
export const CLAUDE_NO_TOOLS_ARGS = [
|
|
13
|
+
"--safe-mode", "--no-session-persistence", "--tools", "", "--permission-mode", "dontAsk",
|
|
14
|
+
"--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}',
|
|
15
|
+
];
|
|
16
|
+
/** `--model <model>`, or nothing for the CLI's own default. */
|
|
17
|
+
export const claudeModelArgs = (model) => (model === "default" ? [] : ["--model", model]);
|
|
18
|
+
/** What a failed call's output says when this computer's Claude isn't signed in. */
|
|
19
|
+
export const CLAUDE_SIGNED_OUT = /not logged in|login|authenticat/i;
|
|
20
|
+
/** `claude -p --output-format json` stdout as its one object, or null. */
|
|
21
|
+
export function claudeJsonBody(stdout, cap) {
|
|
22
|
+
const parsed = strictJsonParse(Buffer.from(stdout, "utf8"), cap, 12);
|
|
23
|
+
return parsed.ok && typeof parsed.value === "object" && parsed.value !== null && !Array.isArray(parsed.value) ? parsed.value : null;
|
|
24
|
+
}
|
|
25
|
+
/** Runs `work` in a fresh temporary folder named `<prefix>…`, removed afterwards whatever happens. */
|
|
26
|
+
export async function inScratchDir(prefix, work) {
|
|
27
|
+
const dir = mkdtempSync(join(tmpdir(), prefix));
|
|
28
|
+
try {
|
|
29
|
+
return await work(dir);
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
rmSync(dir, { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/cli-http.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { Store } from './store.js';
|
|
|
3
3
|
import type { Principal } from './operate.js';
|
|
4
4
|
import { type RemoteCommandLookup } from './remote-command.js';
|
|
5
5
|
import { type Admission, type BudgetLimit } from './request-budget.js';
|
|
6
|
-
import type { SessionRunner } from './session-remote.js';
|
|
7
6
|
/**
|
|
8
7
|
* `POST /api/cli` (remote CLI, Phase 1): a teammate's laptop sends the argv it would have run locally, and the central
|
|
9
8
|
* server runs that same command as that person. There is no per-resource API here and no second policy: the transport
|
|
@@ -12,10 +11,6 @@ import type { SessionRunner } from './session-remote.js';
|
|
|
12
11
|
*
|
|
13
12
|
* Only `Authorization: Bearer so_…` is accepted — never a password, cookie or the owner's saved login — and a request
|
|
14
13
|
* carrying a browser Origin is refused, so a page can never drive it.
|
|
15
|
-
*
|
|
16
|
-
* `session …` (D7) is the one command the server answers through its own owner rather than runOperateAs: native
|
|
17
|
-
* session operations need the server's live coding workspace (session-remote.ts). They are admitted, budgeted and
|
|
18
|
-
* refused here exactly like every other command.
|
|
19
14
|
*/
|
|
20
15
|
/** Who a remote command runs as, and the runner: operate.ts's own types, so a change there breaks this build instead of
|
|
21
16
|
* drifting. Type-only: the value is loaded lazily through `run` (operate.ts imports the server that imports this). */
|
|
@@ -50,8 +45,6 @@ export type CliHttpOptions = {
|
|
|
50
45
|
modeOf?: RemoteCommandLookup | undefined;
|
|
51
46
|
/** The token's request budget (request-budget.ts), charged once per request before its body is read. */
|
|
52
47
|
admit?: ((principal: Principal) => Admission) | undefined;
|
|
53
|
-
/** Native session operations (`session …`), run by the server's session owner; absent where none is ready. */
|
|
54
|
-
session?: SessionRunner | undefined;
|
|
55
48
|
store: Store;
|
|
56
49
|
};
|
|
57
50
|
/** Decode the request's shape and byte limits. The endpoint checks file references against its command policy. */
|
package/dist/cli-http.js
CHANGED
|
@@ -123,8 +123,6 @@ export async function handleCliHttp(request, response, options) {
|
|
|
123
123
|
const parsed = cliRequest(value);
|
|
124
124
|
if ('problem' in parsed)
|
|
125
125
|
return reject(parsed.tooLarge ? 413 : 400, 'invalid-request', parsed.problem);
|
|
126
|
-
if (parsed.argv[0] === 'session')
|
|
127
|
-
return runSession(response, options, principal, parsed);
|
|
128
126
|
const row = (options.modeOf ?? contractRow)(parsed.argv);
|
|
129
127
|
if (row?.mode === 'step-up')
|
|
130
128
|
return reject(403, 'step-up', STEP_UP_MESSAGE);
|
|
@@ -161,22 +159,3 @@ export async function handleCliHttp(request, response, options) {
|
|
|
161
159
|
send(response, 200, { exitCode, stdout: out.join(''), stderr: '' });
|
|
162
160
|
return true;
|
|
163
161
|
}
|
|
164
|
-
/** `session …`: the prompt is the request's one file; the session owner answers one session envelope on stdout. */
|
|
165
|
-
async function runSession(response, options, principal, parsed) {
|
|
166
|
-
const refuse = (status, code, message) => { send(response, status, { ok: false, code, message }); return true; };
|
|
167
|
-
if (options.session === undefined)
|
|
168
|
-
return refuse(501, 'remote-unavailable', 'This server cannot run session commands. Update it, or use the console.');
|
|
169
|
-
const at = parsed.argv.indexOf('--file'), file = at < 0 ? undefined : parsed.argv[at + 1];
|
|
170
|
-
if (Object.keys(parsed.files).some(path => path !== file))
|
|
171
|
-
return refuse(400, 'invalid-request', 'Each file must name a declared input file argument.');
|
|
172
|
-
const out = [];
|
|
173
|
-
let exitCode;
|
|
174
|
-
try {
|
|
175
|
-
exitCode = await options.session(principal, parsed.argv, parsed.files, line => { out.push(`${line}\n`); });
|
|
176
|
-
}
|
|
177
|
-
catch {
|
|
178
|
-
return refuse(500, 'command-failed', 'The command stopped unexpectedly on the server. Check its state before running it again.');
|
|
179
|
-
}
|
|
180
|
-
send(response, 200, { exitCode, stdout: out.join(''), stderr: '' });
|
|
181
|
-
return true;
|
|
182
|
-
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { previewGithubRepo, cloneGithubRepo } from "./onboard.js";
|
|
14
14
|
import { type QuickStartSeams } from "./quick-start.js";
|
|
15
|
-
import { type
|
|
16
|
-
import { type TeamCliOptions } from "./team-cli.js";
|
|
15
|
+
import { type ConnectOptions } from "./connect-cli.js";
|
|
17
16
|
import { type RemoteExecOptions } from "./remote-exec.js";
|
|
18
17
|
import type { UpdateCliDeps } from "./toolroll-update-cli.js";
|
|
19
18
|
import type { ReleaseCliDeps } from "./release-cli.js";
|
|
@@ -47,19 +46,13 @@ type Write = (line: string) => void;
|
|
|
47
46
|
export { HELP };
|
|
48
47
|
export declare function parseArgs(argv: readonly string[]): ParseResult;
|
|
49
48
|
/** The commands that operate the queue rather than report on the world. */
|
|
50
|
-
/** Every verb the CLI routes OUTSIDE runOperate. Exported (with
|
|
51
|
-
* OPERATE_COMMANDS) so the declared command guide's drift tests compare
|
|
52
|
-
* exact sets instead of regex-reading source. "" is the no-verb report,
|
|
53
|
-
* which answers as `scan`. */
|
|
54
|
-
export declare const TOP_LEVEL_COMMANDS: readonly string[];
|
|
55
49
|
export declare const OPERATE_COMMANDS: Set<string>;
|
|
56
50
|
/** binSource exists so tests can exercise linking without depending on a build. */
|
|
57
51
|
export type MainOptions = {
|
|
58
52
|
binSource?: string;
|
|
59
53
|
operate?: OperateOptions;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
/** Injected by tests: the remote CLI's profile, fetch and output streams (defaults to `team`'s profile seams). */
|
|
54
|
+
connect?: ConnectOptions;
|
|
55
|
+
/** Injected by tests: the remote CLI's profile, fetch and output streams (defaults to `connect`'s profile seams). */
|
|
63
56
|
remote?: RemoteExecOptions;
|
|
64
57
|
/** Injected by tests: the gh-facing halves of `repos add-from-github` —
|
|
65
58
|
* the verb's parsing, gating, and enrollment are what CLI tests prove;
|
package/dist/cli.js
CHANGED
|
@@ -26,9 +26,8 @@ import { parseGithubRepo, previewGithubRepo, cloneGithubRepo, isLargeRepo } from
|
|
|
26
26
|
import { run as execRun } from "./exec.js";
|
|
27
27
|
import { isBareInvocation, runQuickStart } from "./quick-start.js";
|
|
28
28
|
import { COMMAND_GUIDE, SURFACE_NOTES, SURFACE_SCHEMA_VERSION } from "./surface.js";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import { deprecationWarning } from "./deprecations.js";
|
|
29
|
+
import { hasCentralProfile, maybeRunConnectCommand } from "./connect-cli.js";
|
|
30
|
+
import { removedCommand, removedMessage } from "./deprecations.js";
|
|
32
31
|
import { maybeRunRemoteCommand } from "./remote-exec.js";
|
|
33
32
|
import { discover, inspectAll } from "./discover.js";
|
|
34
33
|
import { readPulls } from "./pulls.js";
|
|
@@ -107,16 +106,6 @@ export function parseArgs(argv) {
|
|
|
107
106
|
};
|
|
108
107
|
}
|
|
109
108
|
/** The commands that operate the queue rather than report on the world. */
|
|
110
|
-
/** Every verb the CLI routes OUTSIDE runOperate. Exported (with
|
|
111
|
-
* OPERATE_COMMANDS) so the declared command guide's drift tests compare
|
|
112
|
-
* exact sets instead of regex-reading source. "" is the no-verb report,
|
|
113
|
-
* which answers as `scan`. */
|
|
114
|
-
export const TOP_LEVEL_COMMANDS = [
|
|
115
|
-
"", "scan", "pulls", "graph", "repos", "repos add", "repos remove", "repos add-from-github",
|
|
116
|
-
"link", "unlink", "update", "release", "contract", "skills list", "skills get", "skills install", "demo",
|
|
117
|
-
...SESSION_CLI_ACTIONS.map(action => `session ${action}`),
|
|
118
|
-
...TEAM_CLI_ACTIONS,
|
|
119
|
-
];
|
|
120
109
|
export const OPERATE_COMMANDS = new Set([
|
|
121
110
|
"up",
|
|
122
111
|
// Only `lead token` reaches here; the central service's lead verbs answer first.
|
|
@@ -310,21 +299,29 @@ async function dispatch(argv, write, mainOptions) {
|
|
|
310
299
|
// hidden from help and the contract, before anything remote or local opens, changing nothing.
|
|
311
300
|
if (argv[0] === "routine")
|
|
312
301
|
return routinesMoved(argv, write);
|
|
302
|
+
// D5: the central team CLI and native sessions are gone; each old invocation says so and changes nothing.
|
|
303
|
+
const connectOptions = mainOptions.connect ?? mainOptions.remote;
|
|
304
|
+
const removed = removedCommand(argv, () => hasCentralProfile(connectOptions ?? {}));
|
|
305
|
+
if (removed !== null) {
|
|
306
|
+
const message = removedMessage(removed.named, removed.kind);
|
|
307
|
+
write(argv.includes("--json") ? envelopeJson({ ok: false, command: removed.named, reason: "removed", message }) : message);
|
|
308
|
+
return 2;
|
|
309
|
+
}
|
|
313
310
|
// A saved API token sends commands marked remote to the central server, before anything local opens.
|
|
314
311
|
// Command families below load when their command runs, so one command never pays for the others.
|
|
315
312
|
if (argv[0] === 'health')
|
|
316
|
-
return (await import("./health-cli.js")).runHealthCommand(argv.slice(1), write, mainOptions.
|
|
313
|
+
return (await import("./health-cli.js")).runHealthCommand(argv.slice(1), write, mainOptions.connect ?? mainOptions.remote);
|
|
317
314
|
// This computer's own service, read here and never on a central server.
|
|
318
315
|
if (argv[0] === 'doctor')
|
|
319
316
|
return (await import("./doctor.js")).runDoctorCommand(argv.slice(1), write);
|
|
320
|
-
const central = await maybeRunRemoteCommand(argv, mainOptions.remote ?? mainOptions.
|
|
317
|
+
const central = await maybeRunRemoteCommand(argv, mainOptions.remote ?? mainOptions.connect);
|
|
321
318
|
if (typeof central === "number")
|
|
322
319
|
return central;
|
|
323
320
|
if (central !== null)
|
|
324
321
|
argv = central.local;
|
|
325
|
-
const
|
|
326
|
-
if (
|
|
327
|
-
return
|
|
322
|
+
const connected = await maybeRunConnectCommand(argv, write, mainOptions.connect);
|
|
323
|
+
if (connected !== null)
|
|
324
|
+
return connected;
|
|
328
325
|
if ((argv[0] === "chat" || argv[0] === "brief") && argv.includes("--local"))
|
|
329
326
|
argv = argv.filter(arg => arg !== "--local");
|
|
330
327
|
const [first, ...rest] = argv;
|
|
@@ -345,12 +342,6 @@ async function dispatch(argv, write, mainOptions) {
|
|
|
345
342
|
}
|
|
346
343
|
if (first === "contract")
|
|
347
344
|
return runContractCommand(rest, write);
|
|
348
|
-
if (first === "session") {
|
|
349
|
-
// D5: native coding sessions are deprecated. They still run this release; each use says so once, on stderr.
|
|
350
|
-
(mainOptions.session?.stderr ?? (line => process.stderr.write(`${line}\n`)))(deprecationWarning(`session ${rest.find(arg => !arg.startsWith("-")) ?? ""}`.trim(), "session"));
|
|
351
|
-
// D7: it runs on the saved connection over /api/cli, signed in with that profile's API token.
|
|
352
|
-
return runSessionCommand(rest, write, { ...(mainOptions.remote ?? mainOptions.team), ...mainOptions.session });
|
|
353
|
-
}
|
|
354
345
|
if (first === "demo")
|
|
355
346
|
return runDemoCommand(rest, write);
|
|
356
347
|
if (first === "skills")
|
package/dist/coding-provider.js
CHANGED
|
@@ -6,6 +6,7 @@ import { createContainer, currentContainment } from "./containment.js";
|
|
|
6
6
|
import { processMayBeAlive } from "./process-liveness.js";
|
|
7
7
|
import { observeProcessTree, sampleProcessTree, stopProcessTree } from "./process-tree.js";
|
|
8
8
|
import { isOwnEnv } from "./names.js";
|
|
9
|
+
import { sleep } from "./sleep.js";
|
|
9
10
|
/** A response from the server, distinct from an ambiguous transport failure. */
|
|
10
11
|
export class CodingProviderRequestError extends Error {
|
|
11
12
|
code;
|
|
@@ -374,7 +375,7 @@ export function createCodexCodingProvider(options = {}) {
|
|
|
374
375
|
const absent = () => (owned.pid === undefined || !processMayBeAlive(owned.pid, process.platform !== "win32")) && [...descendants.values()].every(({ pid, group }) => !processMayBeAlive(pid, group));
|
|
375
376
|
const until = Date.now() + EXIT_GRACE_MS;
|
|
376
377
|
while (!ancestryUnknown && !absent() && Date.now() < until)
|
|
377
|
-
await
|
|
378
|
+
await sleep(25);
|
|
378
379
|
clean = clean && !ancestryUnknown && absent();
|
|
379
380
|
}
|
|
380
381
|
if (!clean)
|
package/dist/coding-workspace.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { mkdirSync, chmodSync, realpathSync } from 'node:fs';
|
|
3
|
-
import { dirname, join
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
4
|
import { hostname } from 'node:os';
|
|
5
5
|
import { provenDeadByBootChange } from './boot-identity.js';
|
|
6
6
|
import { containerEmptiness } from './container-state.js';
|