agent-dealer 0.2.0 → 1.0.0
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/bundle/server/dist/adapters/agent-deck-bind.js +443 -0
- package/bundle/server/dist/adapters/agent-deck-bind.test.js +454 -0
- package/bundle/server/dist/adapters/agent-deck.js +38 -50
- package/bundle/server/dist/adapters/agent-deck.test.js +63 -0
- package/bundle/server/dist/adapters/agent-health.js +230 -33
- package/bundle/server/dist/adapters/agent-health.test.js +237 -0
- package/bundle/server/dist/adapters/authority-lifecycle.js +289 -0
- package/bundle/server/dist/adapters/codex-scoped-config.js +103 -0
- package/bundle/server/dist/adapters/execution-authority.js +103 -0
- package/bundle/server/dist/adapters/execution-authority.test.js +166 -0
- package/bundle/server/dist/adapters/external.js +0 -1
- package/bundle/server/dist/adapters/git-worktree.js +514 -0
- package/bundle/server/dist/adapters/git-worktree.test.js +502 -0
- package/bundle/server/dist/adapters/github.js +170 -0
- package/bundle/server/dist/adapters/github.test.js +205 -0
- package/bundle/server/dist/adapters/linear-inbox.js +199 -30
- package/bundle/server/dist/adapters/linear-inbox.test.js +35 -0
- package/bundle/server/dist/adapters/linear-sync.js +3 -101
- package/bundle/server/dist/adapters/managed-repo.js +168 -0
- package/bundle/server/dist/adapters/managed-repo.test.js +63 -0
- package/bundle/server/dist/adapters/outbound-delivery.js +83 -0
- package/bundle/server/dist/adapters/outbound-delivery.test.js +53 -0
- package/bundle/server/dist/adapters/reflect-authority.js +43 -0
- package/bundle/server/dist/cli-env.js +26 -0
- package/bundle/server/dist/cli-env.test.js +14 -1
- package/bundle/server/dist/coordinator/abort.integration.test.js +74 -0
- package/bundle/server/dist/coordinator/admission.js +330 -0
- package/bundle/server/dist/coordinator/admission.test.js +338 -0
- package/bundle/server/dist/coordinator/args.js +128 -0
- package/bundle/server/dist/coordinator/args.test.js +132 -0
- package/bundle/server/dist/coordinator/authority-recovery.integration.test.js +661 -0
- package/bundle/server/dist/coordinator/auto-merge.integration.test.js +363 -0
- package/bundle/server/dist/coordinator/auto-merge.js +286 -0
- package/bundle/server/dist/coordinator/auto-merge.timeout.test.js +14 -0
- package/bundle/server/dist/coordinator/branch-progress.js +81 -0
- package/bundle/server/dist/coordinator/clock-jump.js +98 -0
- package/bundle/server/dist/coordinator/commands.js +1088 -0
- package/bundle/server/dist/coordinator/commands.test.js +554 -0
- package/bundle/server/dist/coordinator/deck-outage-config.js +32 -0
- package/bundle/server/dist/coordinator/deck-outage.integration.test.js +200 -0
- package/bundle/server/dist/coordinator/dependencies.js +242 -0
- package/bundle/server/dist/coordinator/dependency-readiness.integration.test.js +535 -0
- package/bundle/server/dist/coordinator/developer-effect.js +959 -0
- package/bundle/server/dist/coordinator/developer-effect.test.js +1143 -0
- package/bundle/server/dist/coordinator/dispatcher.js +58 -0
- package/bundle/server/dist/coordinator/dispatcher.test.js +81 -0
- package/bundle/server/dist/coordinator/effect-registry.js +17 -0
- package/bundle/server/dist/coordinator/execution-env.integration.test.js +122 -0
- package/bundle/server/dist/coordinator/failure-reason.js +273 -0
- package/bundle/server/dist/coordinator/failure-reason.test.js +234 -0
- package/bundle/server/dist/coordinator/guidance.js +33 -0
- package/bundle/server/dist/coordinator/guidance.test.js +110 -0
- package/bundle/server/dist/coordinator/human-resolution.js +88 -0
- package/bundle/server/dist/coordinator/human-resolution.test.js +72 -0
- package/bundle/server/dist/coordinator/intent-forecast.js +13 -0
- package/bundle/server/dist/coordinator/intent-forecast.test.js +34 -0
- package/bundle/server/dist/coordinator/latest-failure.js +100 -0
- package/bundle/server/dist/coordinator/latest-failure.test.js +125 -0
- package/bundle/server/dist/coordinator/metrics.js +35 -0
- package/bundle/server/dist/coordinator/metrics.test.js +52 -0
- package/bundle/server/dist/coordinator/permissions.js +127 -0
- package/bundle/server/dist/coordinator/permissions.test.js +184 -0
- package/bundle/server/dist/coordinator/presumed-dead-budget.integration.test.js +146 -0
- package/bundle/server/dist/coordinator/process-liveness.js +191 -0
- package/bundle/server/dist/coordinator/profile-snapshot.js +20 -0
- package/bundle/server/dist/coordinator/profile-snapshot.test.js +118 -0
- package/bundle/server/dist/coordinator/projection.js +147 -0
- package/bundle/server/dist/coordinator/projection.test.js +121 -0
- package/bundle/server/dist/coordinator/prompts.js +164 -0
- package/bundle/server/dist/coordinator/prompts.test.js +177 -0
- package/bundle/server/dist/coordinator/queue-by-default.integration.test.js +271 -0
- package/bundle/server/dist/coordinator/reclaim-republish.integration.test.js +372 -0
- package/bundle/server/dist/coordinator/recovery.js +466 -0
- package/bundle/server/dist/coordinator/recovery.test.js +164 -0
- package/bundle/server/dist/coordinator/reflect-trigger.js +218 -0
- package/bundle/server/dist/coordinator/reflect-trigger.test.js +324 -0
- package/bundle/server/dist/coordinator/repair-cycle.integration.test.js +234 -0
- package/bundle/server/dist/coordinator/restart-recovery.integration.test.js +363 -0
- package/bundle/server/dist/coordinator/reviewer-effect.js +507 -0
- package/bundle/server/dist/coordinator/reviewer-effect.test.js +612 -0
- package/bundle/server/dist/coordinator/reviewer-result.js +38 -0
- package/bundle/server/dist/coordinator/reviewer-result.test.js +45 -0
- package/bundle/server/dist/coordinator/routing.js +160 -0
- package/bundle/server/dist/coordinator/routing.test.js +336 -0
- package/bundle/server/dist/coordinator/session-lifecycle.integration.test.js +127 -0
- package/bundle/server/dist/coordinator/session-lifecycle.js +280 -0
- package/bundle/server/dist/coordinator/session-lifecycle.test.js +121 -0
- package/bundle/server/dist/coordinator/session-progress.js +351 -0
- package/bundle/server/dist/coordinator/session-progress.test.js +190 -0
- package/bundle/server/dist/coordinator/session-timeouts.js +33 -0
- package/bundle/server/dist/coordinator/sleep-recovery.integration.test.js +233 -0
- package/bundle/server/dist/coordinator/spawn.js +49 -0
- package/bundle/server/dist/coordinator/sync-issue-base-branch.js +18 -0
- package/bundle/server/dist/coordinator/usage-cap-config.js +10 -0
- package/bundle/server/dist/coordinator/usage-cap-defer.js +189 -0
- package/bundle/server/dist/coordinator/usage-cap.integration.test.js +193 -0
- package/bundle/server/dist/coordinator/usage.js +40 -0
- package/bundle/server/dist/coordinator/usage.test.js +69 -0
- package/bundle/server/dist/coordinator/verification-receipt.js +333 -0
- package/bundle/server/dist/coordinator/verification-receipt.test.js +382 -0
- package/bundle/server/dist/coordinator/worker-loop.js +363 -0
- package/bundle/server/dist/coordinator/worker-loop.test.js +327 -0
- package/bundle/server/dist/coordinator/workflow-instances.test.js +45 -0
- package/bundle/server/dist/coordinator/worktree-owner-liveness.js +82 -0
- package/bundle/server/dist/coordinator/worktree-owner-liveness.test.js +199 -0
- package/bundle/server/dist/db/artifacts-issue-columns.test.js +18 -0
- package/bundle/server/dist/db/authority-attempts-stale-at-migration.test.js +65 -0
- package/bundle/server/dist/db/idempotency-index-migration.test.js +57 -0
- package/bundle/server/dist/db/index.js +359 -7
- package/bundle/server/dist/db/linear-inbox-unlock.test.js +67 -0
- package/bundle/server/dist/db/migrate-to-issues.js +824 -0
- package/bundle/server/dist/db/migrate-to-issues.test.js +842 -0
- package/bundle/server/dist/db/schema.sql +311 -1
- package/bundle/server/dist/db/schema.test.js +66 -0
- package/bundle/server/dist/db/worker-session-pid-migration.test.js +112 -0
- package/bundle/server/dist/dev-review-cli-happy-path.integration.test.js +338 -0
- package/bundle/server/dist/direct-start-liveness.integration.test.js +350 -0
- package/bundle/server/dist/index.js +58 -5
- package/bundle/server/dist/paths.js +40 -4
- package/bundle/server/dist/paths.test.js +42 -0
- package/bundle/server/dist/pid-marker-claim-child.js +18 -0
- package/bundle/server/dist/pid-marker-hold-arbiter-child.js +24 -0
- package/bundle/server/dist/pid-marker.js +141 -0
- package/bundle/server/dist/pid-marker.test.js +208 -0
- package/bundle/server/dist/queue/approve-deliver.js +128 -28
- package/bundle/server/dist/queue/reflect-schedule.js +25 -0
- package/bundle/server/dist/queue/result-qa.js +1 -1
- package/bundle/server/dist/queue/send-gate.test.js +172 -36
- package/bundle/server/dist/repository/agents.js +55 -18
- package/bundle/server/dist/repository/artifacts-for-issue.js +42 -0
- package/bundle/server/dist/repository/artifacts-for-issue.test.js +48 -0
- package/bundle/server/dist/repository/artifacts.js +47 -0
- package/bundle/server/dist/repository/artifacts.test.js +48 -0
- package/bundle/server/dist/repository/authority-attempts.js +189 -0
- package/bundle/server/dist/repository/findings.js +70 -0
- package/bundle/server/dist/repository/findings.test.js +68 -0
- package/bundle/server/dist/repository/human-actions.js +142 -0
- package/bundle/server/dist/repository/human-actions.test.js +123 -0
- package/bundle/server/dist/repository/intake-settings.js +5 -59
- package/bundle/server/dist/repository/issues.js +305 -0
- package/bundle/server/dist/repository/issues.test.js +69 -0
- package/bundle/server/dist/repository/outbound-drafts.js +28 -0
- package/bundle/server/dist/repository/queue-entries.js +162 -0
- package/bundle/server/dist/repository/review-publications.js +153 -0
- package/bundle/server/dist/repository/review-publications.test.js +78 -0
- package/bundle/server/dist/repository/runs-external-id.test.js +5 -3
- package/bundle/server/dist/repository/runs.js +4 -211
- package/bundle/server/dist/repository/runtime-availability.js +54 -0
- package/bundle/server/dist/repository/usage-events.js +60 -0
- package/bundle/server/dist/repository/usage-events.test.js +56 -0
- package/bundle/server/dist/repository/work-items.js +305 -0
- package/bundle/server/dist/repository/work-items.test.js +142 -0
- package/bundle/server/dist/repository/worker-sessions.js +219 -0
- package/bundle/server/dist/repository/worker-sessions.test.js +60 -0
- package/bundle/server/dist/repository/workflow-events.js +193 -0
- package/bundle/server/dist/repository/workflow-events.test.js +89 -0
- package/bundle/server/dist/resolve-tsx-bin.js +19 -0
- package/bundle/server/dist/routes/human-actions.js +68 -0
- package/bundle/server/dist/routes/human-actions.test.js +284 -0
- package/bundle/server/dist/routes/index.js +29 -496
- package/bundle/server/dist/routes/issues.js +240 -0
- package/bundle/server/dist/routes/issues.test.js +451 -0
- package/bundle/server/dist/routes/queue.js +41 -0
- package/bundle/server/dist/routes/queue.test.js +78 -0
- package/bundle/server/dist/runners/claude-args.js +18 -30
- package/bundle/server/dist/runners/claude.js +19 -163
- package/bundle/server/dist/runners/claude.test.js +15 -70
- package/bundle/server/dist/runners/codex-args.js +59 -0
- package/bundle/server/dist/runners/codex-args.test.js +72 -0
- package/bundle/server/dist/runners/codex-jsonl.js +150 -0
- package/bundle/server/dist/runners/codex-jsonl.test.js +36 -0
- package/bundle/server/dist/runners/codex-resume.test.js +19 -0
- package/bundle/server/dist/runners/codex.js +46 -0
- package/bundle/server/dist/runners/models-codex.test.js +14 -0
- package/bundle/server/dist/runners/models.js +53 -1
- package/bundle/server/dist/runners/persist.js +4 -1
- package/bundle/server/dist/runners/process-registry.js +11 -0
- package/bundle/server/dist/runners/prompts.js +13 -280
- package/bundle/server/dist/runners/prompts.test.js +22 -153
- package/bundle/server/dist/runners/qa.js +17 -1
- package/bundle/server/dist/runners/reflect.js +4 -16
- package/bundle/server/dist/runners/spawn-cli.js +197 -0
- package/bundle/server/dist/runners/spawn-cli.test.js +130 -0
- package/bundle/server/dist/runners/stream-json.js +0 -141
- package/bundle/server/dist/runners/usage-cap.js +242 -0
- package/bundle/server/dist/runners/usage-cap.test.js +97 -0
- package/bundle/server/dist/server-liveness.js +37 -0
- package/bundle/server/dist/server-liveness.test.js +82 -0
- package/bundle/server/dist/static-ui.test.js +44 -0
- package/bundle/server/package.json +4 -2
- package/bundle/server/static-ui/assets/index-BTtPGYpY.js +60 -0
- package/bundle/server/static-ui/assets/index-BxTx4b1d.css +1 -0
- package/bundle/server/static-ui/index.html +2 -2
- package/bundle/shared/dist/agents.d.ts +262 -133
- package/bundle/shared/dist/agents.js +85 -19
- package/bundle/shared/dist/cursor-auth-health.d.ts +12 -0
- package/bundle/shared/dist/cursor-auth-health.js +36 -0
- package/bundle/shared/dist/cursor-auth-health.test.d.ts +1 -0
- package/bundle/shared/dist/cursor-auth-health.test.js +40 -0
- package/bundle/shared/dist/findings.d.ts +46 -0
- package/bundle/shared/dist/findings.js +17 -0
- package/bundle/shared/dist/findings.test.d.ts +1 -0
- package/bundle/shared/dist/findings.test.js +20 -0
- package/bundle/shared/dist/github-repo.d.ts +28 -0
- package/bundle/shared/dist/github-repo.js +98 -0
- package/bundle/shared/dist/github-repo.test.d.ts +1 -0
- package/bundle/shared/dist/github-repo.test.js +32 -0
- package/bundle/shared/dist/human-actions.d.ts +66 -0
- package/bundle/shared/dist/human-actions.js +51 -0
- package/bundle/shared/dist/human-actions.test.d.ts +1 -0
- package/bundle/shared/dist/human-actions.test.js +57 -0
- package/bundle/shared/dist/index.d.ts +567 -757
- package/bundle/shared/dist/index.js +22 -36
- package/bundle/shared/dist/issues.d.ts +385 -0
- package/bundle/shared/dist/issues.js +169 -0
- package/bundle/shared/dist/issues.test.d.ts +1 -0
- package/bundle/shared/dist/issues.test.js +72 -0
- package/bundle/shared/dist/outbound-draft.d.ts +6 -0
- package/bundle/shared/dist/outbound-draft.js +4 -0
- package/bundle/shared/dist/plan-triage.d.ts +6 -6
- package/bundle/shared/dist/profile-snapshot.d.ts +154 -0
- package/bundle/shared/dist/profile-snapshot.js +145 -0
- package/bundle/shared/dist/profile-snapshot.test.d.ts +1 -0
- package/bundle/shared/dist/profile-snapshot.test.js +67 -0
- package/bundle/shared/dist/queue-entries.d.ts +89 -0
- package/bundle/shared/dist/queue-entries.js +19 -0
- package/bundle/shared/dist/result-qa.d.ts +2 -2
- package/bundle/shared/dist/runtime-auth-health.d.ts +66 -0
- package/bundle/shared/dist/runtime-auth-health.js +186 -0
- package/bundle/shared/dist/runtime-auth-health.test.d.ts +1 -0
- package/bundle/shared/dist/runtime-auth-health.test.js +200 -0
- package/bundle/shared/dist/runtime-availability.d.ts +51 -0
- package/bundle/shared/dist/runtime-availability.js +13 -0
- package/bundle/shared/dist/runtime.d.ts +1 -1
- package/bundle/shared/dist/runtime.js +1 -1
- package/bundle/shared/dist/usage-events.d.ts +36 -0
- package/bundle/shared/dist/usage-events.js +13 -0
- package/bundle/shared/dist/worker-sessions.d.ts +140 -0
- package/bundle/shared/dist/worker-sessions.js +70 -0
- package/bundle/shared/dist/worker-sessions.test.d.ts +1 -0
- package/bundle/shared/dist/worker-sessions.test.js +39 -0
- package/bundle/shared/dist/workflow.d.ts +78 -0
- package/bundle/shared/dist/workflow.js +59 -0
- package/bundle/shared/dist/workflow.test.d.ts +1 -0
- package/bundle/shared/dist/workflow.test.js +61 -0
- package/bundle/shared/package.json +1 -1
- package/dist/action.d.ts +1 -0
- package/dist/action.js +50 -0
- package/dist/action.test.d.ts +1 -0
- package/dist/action.test.js +70 -0
- package/dist/agent.d.ts +1 -0
- package/dist/agent.js +17 -0
- package/dist/agent.test.d.ts +1 -0
- package/dist/agent.test.js +19 -0
- package/dist/cli-check.d.ts +7 -0
- package/dist/cli-check.js +40 -0
- package/dist/doctor.js +59 -1
- package/dist/http.d.ts +5 -0
- package/dist/http.js +19 -0
- package/dist/index.js +28 -0
- package/dist/issue.d.ts +35 -0
- package/dist/issue.js +143 -0
- package/dist/issue.test.d.ts +1 -0
- package/dist/issue.test.js +240 -0
- package/dist/lifecycle.contract.test.d.ts +1 -0
- package/dist/lifecycle.contract.test.js +135 -0
- package/dist/queue.d.ts +11 -0
- package/dist/queue.js +60 -0
- package/dist/queue.test.d.ts +1 -0
- package/dist/queue.test.js +46 -0
- package/dist/test-fetch-stub.d.ts +6 -0
- package/dist/test-fetch-stub.js +31 -0
- package/package.json +2 -1
- package/templates/prod.env.example +1 -1
- package/bundle/server/static-ui/assets/index-B_7S7xJb.js +0 -79
- package/bundle/server/static-ui/assets/index-QYv3zmE6.css +0 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { canTransitionIssue, CreateIssueInput, Issue } from "./issues.js";
|
|
4
|
+
test("issue transitions: ready to developing is allowed", () => {
|
|
5
|
+
assert.equal(canTransitionIssue("ready", "developing"), true);
|
|
6
|
+
});
|
|
7
|
+
test("issue transitions: done is terminal", () => {
|
|
8
|
+
assert.equal(canTransitionIssue("done", "developing"), false);
|
|
9
|
+
assert.equal(canTransitionIssue("done", "closed"), false);
|
|
10
|
+
});
|
|
11
|
+
test("issue transitions: cannot skip straight from ready to final_review", () => {
|
|
12
|
+
assert.equal(canTransitionIssue("ready", "final_review"), false);
|
|
13
|
+
});
|
|
14
|
+
test("issue transitions: final_review can send back to repairing", () => {
|
|
15
|
+
assert.equal(canTransitionIssue("final_review", "repairing"), true);
|
|
16
|
+
});
|
|
17
|
+
test("issue transitions: reviewing can self-loop (stale-review retry at a fresh head)", () => {
|
|
18
|
+
assert.equal(canTransitionIssue("reviewing", "reviewing"), true);
|
|
19
|
+
});
|
|
20
|
+
test("Issue schema parses a well-formed issue", () => {
|
|
21
|
+
const issue = {
|
|
22
|
+
id: "11111111-1111-1111-1111-111111111111",
|
|
23
|
+
source: "manual",
|
|
24
|
+
externalId: null,
|
|
25
|
+
externalLabel: null,
|
|
26
|
+
externalUrl: null,
|
|
27
|
+
title: "Fix login bug",
|
|
28
|
+
description: null,
|
|
29
|
+
acceptanceCriteria: null,
|
|
30
|
+
repo: "acme/app",
|
|
31
|
+
baseBranch: "main",
|
|
32
|
+
status: "ready",
|
|
33
|
+
currentOwner: "system",
|
|
34
|
+
currentIntent: null,
|
|
35
|
+
developerAgentId: "22222222-2222-2222-2222-222222222222",
|
|
36
|
+
reviewerAgentId: "33333333-3333-3333-3333-333333333333",
|
|
37
|
+
maxReviewRounds: 3,
|
|
38
|
+
currentRound: 1,
|
|
39
|
+
maxInfraAttempts: 3,
|
|
40
|
+
infraAttempts: 0,
|
|
41
|
+
branch: null,
|
|
42
|
+
baseSha: null,
|
|
43
|
+
headSha: null,
|
|
44
|
+
prNumber: null,
|
|
45
|
+
prUrl: null,
|
|
46
|
+
autoMerge: false,
|
|
47
|
+
createdAt: new Date().toISOString(),
|
|
48
|
+
updatedAt: new Date().toISOString()
|
|
49
|
+
};
|
|
50
|
+
assert.deepStrictEqual(Issue.parse(issue), issue);
|
|
51
|
+
});
|
|
52
|
+
test("CreateIssueInput defaults baseBranch to main and maxReviewRounds to 3", () => {
|
|
53
|
+
const parsed = CreateIssueInput.parse({
|
|
54
|
+
title: "Fix login bug",
|
|
55
|
+
repo: "acme/app",
|
|
56
|
+
developerAgentId: "22222222-2222-2222-2222-222222222222",
|
|
57
|
+
reviewerAgentId: "33333333-3333-3333-3333-333333333333"
|
|
58
|
+
});
|
|
59
|
+
assert.equal(parsed.baseBranch, "main");
|
|
60
|
+
assert.equal(parsed.maxReviewRounds, 3);
|
|
61
|
+
assert.equal(parsed.source, "manual");
|
|
62
|
+
assert.equal(parsed.autoMerge, false);
|
|
63
|
+
assert.equal(parsed.repo, "github.com/acme/app");
|
|
64
|
+
});
|
|
65
|
+
test("CreateIssueInput rejects local filesystem paths (NOT-149)", () => {
|
|
66
|
+
assert.throws(() => CreateIssueInput.parse({
|
|
67
|
+
title: "Local",
|
|
68
|
+
repo: "/repo",
|
|
69
|
+
developerAgentId: "22222222-2222-2222-2222-222222222222",
|
|
70
|
+
reviewerAgentId: "33333333-3333-3333-3333-333333333333",
|
|
71
|
+
}), /Local filesystem|Invalid/);
|
|
72
|
+
});
|
|
@@ -132,6 +132,10 @@ export declare const OutboundDraftContent: z.ZodObject<{
|
|
|
132
132
|
sentAt: z.ZodOptional<z.ZodString>;
|
|
133
133
|
rejectedAt: z.ZodOptional<z.ZodString>;
|
|
134
134
|
bodyMismatch: z.ZodOptional<z.ZodBoolean>;
|
|
135
|
+
/** Count of real delivery attempts made for this draft (NOT-95) — feeds the execution
|
|
136
|
+
* authority idempotency key (`${draftArtifactId}:${deliveryAttempts}`) so a retry after
|
|
137
|
+
* INTERACTION_REQUIRED mints a genuinely distinct authority rather than reusing one. */
|
|
138
|
+
deliveryAttempts: z.ZodDefault<z.ZodNumber>;
|
|
135
139
|
}, "strip", z.ZodTypeAny, {
|
|
136
140
|
status: "pending" | "sent" | "rejected";
|
|
137
141
|
draft: {
|
|
@@ -146,6 +150,7 @@ export declare const OutboundDraftContent: z.ZodObject<{
|
|
|
146
150
|
arguments: Record<string, unknown>;
|
|
147
151
|
};
|
|
148
152
|
};
|
|
153
|
+
deliveryAttempts: number;
|
|
149
154
|
sentAt?: string | undefined;
|
|
150
155
|
rejectedAt?: string | undefined;
|
|
151
156
|
bodyMismatch?: boolean | undefined;
|
|
@@ -166,6 +171,7 @@ export declare const OutboundDraftContent: z.ZodObject<{
|
|
|
166
171
|
sentAt?: string | undefined;
|
|
167
172
|
rejectedAt?: string | undefined;
|
|
168
173
|
bodyMismatch?: boolean | undefined;
|
|
174
|
+
deliveryAttempts?: number | undefined;
|
|
169
175
|
}>;
|
|
170
176
|
export type OutboundDraftContent = z.infer<typeof OutboundDraftContent>;
|
|
171
177
|
/** send_receipt artifact contentJson (PRD §7.3). */
|
|
@@ -23,6 +23,10 @@ export const OutboundDraftContent = z.object({
|
|
|
23
23
|
sentAt: z.string().optional(),
|
|
24
24
|
rejectedAt: z.string().optional(),
|
|
25
25
|
bodyMismatch: z.boolean().optional(),
|
|
26
|
+
/** Count of real delivery attempts made for this draft (NOT-95) — feeds the execution
|
|
27
|
+
* authority idempotency key (`${draftArtifactId}:${deliveryAttempts}`) so a retry after
|
|
28
|
+
* INTERACTION_REQUIRED mints a genuinely distinct authority rather than reusing one. */
|
|
29
|
+
deliveryAttempts: z.number().int().min(0).default(0),
|
|
26
30
|
});
|
|
27
31
|
/** send_receipt artifact contentJson (PRD §7.3). */
|
|
28
32
|
export const SendReceiptContent = z.object({
|
|
@@ -74,8 +74,8 @@ export declare const PlanTriageBlock: z.ZodEffects<z.ZodObject<{
|
|
|
74
74
|
question: string;
|
|
75
75
|
}>, "many">>;
|
|
76
76
|
}, "strip", z.ZodTypeAny, {
|
|
77
|
-
verdict: "trivial" | "needs_review";
|
|
78
77
|
rationale: string;
|
|
78
|
+
verdict: "trivial" | "needs_review";
|
|
79
79
|
questions: {
|
|
80
80
|
options: {
|
|
81
81
|
label: string;
|
|
@@ -85,8 +85,8 @@ export declare const PlanTriageBlock: z.ZodEffects<z.ZodObject<{
|
|
|
85
85
|
question: string;
|
|
86
86
|
}[];
|
|
87
87
|
}, {
|
|
88
|
-
verdict: "trivial" | "needs_review";
|
|
89
88
|
rationale: string;
|
|
89
|
+
verdict: "trivial" | "needs_review";
|
|
90
90
|
questions?: {
|
|
91
91
|
options: {
|
|
92
92
|
label: string;
|
|
@@ -96,8 +96,8 @@ export declare const PlanTriageBlock: z.ZodEffects<z.ZodObject<{
|
|
|
96
96
|
question: string;
|
|
97
97
|
}[] | undefined;
|
|
98
98
|
}>, {
|
|
99
|
-
verdict: "trivial" | "needs_review";
|
|
100
99
|
rationale: string;
|
|
100
|
+
verdict: "trivial" | "needs_review";
|
|
101
101
|
questions: {
|
|
102
102
|
options: {
|
|
103
103
|
label: string;
|
|
@@ -107,8 +107,8 @@ export declare const PlanTriageBlock: z.ZodEffects<z.ZodObject<{
|
|
|
107
107
|
question: string;
|
|
108
108
|
}[];
|
|
109
109
|
}, {
|
|
110
|
-
verdict: "trivial" | "needs_review";
|
|
111
110
|
rationale: string;
|
|
111
|
+
verdict: "trivial" | "needs_review";
|
|
112
112
|
questions?: {
|
|
113
113
|
options: {
|
|
114
114
|
label: string;
|
|
@@ -155,8 +155,8 @@ export declare const PlanTriageContent: z.ZodObject<{
|
|
|
155
155
|
parseFallback: z.ZodBoolean;
|
|
156
156
|
consumed: z.ZodDefault<z.ZodBoolean>;
|
|
157
157
|
}, "strip", z.ZodTypeAny, {
|
|
158
|
-
verdict: "trivial" | "needs_review";
|
|
159
158
|
rationale: string;
|
|
159
|
+
verdict: "trivial" | "needs_review";
|
|
160
160
|
questions: {
|
|
161
161
|
options: {
|
|
162
162
|
label: string;
|
|
@@ -169,8 +169,8 @@ export declare const PlanTriageContent: z.ZodObject<{
|
|
|
169
169
|
consumed: boolean;
|
|
170
170
|
sessionId?: string | undefined;
|
|
171
171
|
}, {
|
|
172
|
-
verdict: "trivial" | "needs_review";
|
|
173
172
|
rationale: string;
|
|
173
|
+
verdict: "trivial" | "needs_review";
|
|
174
174
|
questions: {
|
|
175
175
|
options: {
|
|
176
176
|
label: string;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { WorkerSessionRole } from "./worker-sessions.js";
|
|
3
|
+
/**
|
|
4
|
+
* The effective capabilities a worker session may exercise at the runtime boundary
|
|
5
|
+
* (ADR 0003 §Role permissions). Resolved once when the session is created and frozen
|
|
6
|
+
* into `worker_sessions.profile_snapshot_json` — a later profile edit never changes a
|
|
7
|
+
* queued or running session.
|
|
8
|
+
*
|
|
9
|
+
* Every field here is backed by a control this codebase can actually enforce from
|
|
10
|
+
* outside the worker process — a CLI tool-permission grant (`--allowedTools` /
|
|
11
|
+
* `-s read-only`) or a config-loading boundary (`--ignore-user-config`) — never a
|
|
12
|
+
* denylist over arguments to a capability (e.g. Bash) the worker already holds
|
|
13
|
+
* unrestricted, which a review round proved bypassable (absolute paths, `-C`, wrapper
|
|
14
|
+
* commands, or — for `push` — simply editing the very git config meant to restrict it,
|
|
15
|
+
* since that config lives inside the worktree the same process can already write).
|
|
16
|
+
* `push` / `openPr` are NOT modeled here for that reason: real enforcement needs those
|
|
17
|
+
* credentialed effects to move to a call the worker never makes itself (the coordinator,
|
|
18
|
+
* which already owns "the only component that publishes" for reviews) — developer→PR
|
|
19
|
+
* handoff scope (NOT-61+), not a profile toggle this ticket can honestly ship.
|
|
20
|
+
*
|
|
21
|
+
* Role defaults are the ceiling: a profile's `permissionPolicy` override may only
|
|
22
|
+
* *tighten* a capability (turn it off), never grant one the role does not have.
|
|
23
|
+
*/
|
|
24
|
+
export declare const PermissionPolicy: z.ZodObject<{
|
|
25
|
+
/** developer only: read/write files in its own worktree — CLI tool-grant enforced. */
|
|
26
|
+
worktreeWrite: z.ZodBoolean;
|
|
27
|
+
/** never true in v1 — the coordinator, not the reviewer, publishes the review. */
|
|
28
|
+
publishReview: z.ZodBoolean;
|
|
29
|
+
/** call outbound-mutation deck tools (`call_service_tool`). Off for both roles in v1. */
|
|
30
|
+
outboundMutation: z.ZodBoolean;
|
|
31
|
+
/** resolve human actions / edit workflow topology. Always off for workers. */
|
|
32
|
+
resolveHumanAction: z.ZodBoolean;
|
|
33
|
+
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
worktreeWrite: boolean;
|
|
35
|
+
publishReview: boolean;
|
|
36
|
+
outboundMutation: boolean;
|
|
37
|
+
resolveHumanAction: boolean;
|
|
38
|
+
}, {
|
|
39
|
+
worktreeWrite: boolean;
|
|
40
|
+
publishReview: boolean;
|
|
41
|
+
outboundMutation: boolean;
|
|
42
|
+
resolveHumanAction: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
export type PermissionPolicy = z.infer<typeof PermissionPolicy>;
|
|
45
|
+
/** The subset of a policy a profile may pin off. Absent field = use the role default. */
|
|
46
|
+
export declare const PermissionPolicyOverride: z.ZodObject<{
|
|
47
|
+
worktreeWrite: z.ZodOptional<z.ZodBoolean>;
|
|
48
|
+
outboundMutation: z.ZodOptional<z.ZodBoolean>;
|
|
49
|
+
}, "strict", z.ZodTypeAny, {
|
|
50
|
+
worktreeWrite?: boolean | undefined;
|
|
51
|
+
outboundMutation?: boolean | undefined;
|
|
52
|
+
}, {
|
|
53
|
+
worktreeWrite?: boolean | undefined;
|
|
54
|
+
outboundMutation?: boolean | undefined;
|
|
55
|
+
}>;
|
|
56
|
+
export type PermissionPolicyOverride = z.infer<typeof PermissionPolicyOverride>;
|
|
57
|
+
export declare const DEVELOPER_ROLE_CEILING: PermissionPolicy;
|
|
58
|
+
export declare const REVIEWER_ROLE_CEILING: PermissionPolicy;
|
|
59
|
+
export declare function roleCeiling(role: z.infer<typeof WorkerSessionRole>): PermissionPolicy;
|
|
60
|
+
/**
|
|
61
|
+
* Tiered reasoning / thinking effort for CLIs that expose it (Codex
|
|
62
|
+
* `model_reasoning_effort`, Claude Code `--effort`). Cursor has no separate flag —
|
|
63
|
+
* effort is only expressible inside a parameterized model id.
|
|
64
|
+
*/
|
|
65
|
+
export declare const ReasoningEffort: z.ZodEnum<["low", "medium", "high"]>;
|
|
66
|
+
export type ReasoningEffort = z.infer<typeof ReasoningEffort>;
|
|
67
|
+
/**
|
|
68
|
+
* The immutable execution contract snapshotted onto every `worker_session` at creation
|
|
69
|
+
* (design §`agents` / §"Immutable execution-profile snapshot"). Transcripts, findings,
|
|
70
|
+
* and usage stay owned by the issue/session — completing work never writes anything back
|
|
71
|
+
* into the profile.
|
|
72
|
+
*/
|
|
73
|
+
export declare const ProfileSnapshot: z.ZodObject<{
|
|
74
|
+
/** Schema version so an in-flight session stays reconstructable across format changes. */
|
|
75
|
+
version: z.ZodLiteral<1>;
|
|
76
|
+
agentId: z.ZodNullable<z.ZodString>;
|
|
77
|
+
role: z.ZodEnum<["developer", "reviewer", "legacy"]>;
|
|
78
|
+
runtime: z.ZodNullable<z.ZodEnum<["claude_code", "cursor_local", "codex_local"]>>;
|
|
79
|
+
model: z.ZodNullable<z.ZodString>;
|
|
80
|
+
/**
|
|
81
|
+
* Reasoning effort frozen with the session (NOT-81). `.default(null)` so snapshots
|
|
82
|
+
* written before this field still parse instead of invalidating the whole contract.
|
|
83
|
+
*/
|
|
84
|
+
effort: z.ZodDefault<z.ZodNullable<z.ZodEnum<["low", "medium", "high"]>>>;
|
|
85
|
+
/** Serialized PhaseBudget; null = runtime default (no CLI caps). */
|
|
86
|
+
budgetJson: z.ZodNullable<z.ZodString>;
|
|
87
|
+
permissionPolicy: z.ZodObject<{
|
|
88
|
+
/** developer only: read/write files in its own worktree — CLI tool-grant enforced. */
|
|
89
|
+
worktreeWrite: z.ZodBoolean;
|
|
90
|
+
/** never true in v1 — the coordinator, not the reviewer, publishes the review. */
|
|
91
|
+
publishReview: z.ZodBoolean;
|
|
92
|
+
/** call outbound-mutation deck tools (`call_service_tool`). Off for both roles in v1. */
|
|
93
|
+
outboundMutation: z.ZodBoolean;
|
|
94
|
+
/** resolve human actions / edit workflow topology. Always off for workers. */
|
|
95
|
+
resolveHumanAction: z.ZodBoolean;
|
|
96
|
+
}, "strip", z.ZodTypeAny, {
|
|
97
|
+
worktreeWrite: boolean;
|
|
98
|
+
publishReview: boolean;
|
|
99
|
+
outboundMutation: boolean;
|
|
100
|
+
resolveHumanAction: boolean;
|
|
101
|
+
}, {
|
|
102
|
+
worktreeWrite: boolean;
|
|
103
|
+
publishReview: boolean;
|
|
104
|
+
outboundMutation: boolean;
|
|
105
|
+
resolveHumanAction: boolean;
|
|
106
|
+
}>;
|
|
107
|
+
/** Launch-selected Deck — required for new sessions; null only on legacy in-flight rows. */
|
|
108
|
+
deckId: z.ZodNullable<z.ZodString>;
|
|
109
|
+
purpose: z.ZodNullable<z.ZodString>;
|
|
110
|
+
capturedAt: z.ZodString;
|
|
111
|
+
}, "strip", z.ZodTypeAny, {
|
|
112
|
+
role: "developer" | "reviewer" | "legacy";
|
|
113
|
+
agentId: string | null;
|
|
114
|
+
runtime: "claude_code" | "cursor_local" | "codex_local" | null;
|
|
115
|
+
model: string | null;
|
|
116
|
+
budgetJson: string | null;
|
|
117
|
+
version: 1;
|
|
118
|
+
effort: "low" | "medium" | "high" | null;
|
|
119
|
+
permissionPolicy: {
|
|
120
|
+
worktreeWrite: boolean;
|
|
121
|
+
publishReview: boolean;
|
|
122
|
+
outboundMutation: boolean;
|
|
123
|
+
resolveHumanAction: boolean;
|
|
124
|
+
};
|
|
125
|
+
deckId: string | null;
|
|
126
|
+
purpose: string | null;
|
|
127
|
+
capturedAt: string;
|
|
128
|
+
}, {
|
|
129
|
+
role: "developer" | "reviewer" | "legacy";
|
|
130
|
+
agentId: string | null;
|
|
131
|
+
runtime: "claude_code" | "cursor_local" | "codex_local" | null;
|
|
132
|
+
model: string | null;
|
|
133
|
+
budgetJson: string | null;
|
|
134
|
+
version: 1;
|
|
135
|
+
permissionPolicy: {
|
|
136
|
+
worktreeWrite: boolean;
|
|
137
|
+
publishReview: boolean;
|
|
138
|
+
outboundMutation: boolean;
|
|
139
|
+
resolveHumanAction: boolean;
|
|
140
|
+
};
|
|
141
|
+
deckId: string | null;
|
|
142
|
+
purpose: string | null;
|
|
143
|
+
capturedAt: string;
|
|
144
|
+
effort?: "low" | "medium" | "high" | null | undefined;
|
|
145
|
+
}>;
|
|
146
|
+
export type ProfileSnapshot = z.infer<typeof ProfileSnapshot>;
|
|
147
|
+
export declare function parseProfileSnapshot(json: string | null | undefined): ProfileSnapshot | null;
|
|
148
|
+
/** Serialized `string[]` helpers for the profile list columns. */
|
|
149
|
+
export declare function serializeStringList(list: readonly string[] | null | undefined): string | null;
|
|
150
|
+
export declare function parseStringList(json: string | null | undefined): string[];
|
|
151
|
+
export declare function serializePermissionPolicyOverride(override: PermissionPolicyOverride | null | undefined): string | null;
|
|
152
|
+
export declare function parsePermissionPolicyOverride(json: string | null | undefined): PermissionPolicyOverride | null;
|
|
153
|
+
/** Apply a profile override to a role ceiling. Override can only turn capabilities off. */
|
|
154
|
+
export declare function resolvePermissionPolicy(role: z.infer<typeof WorkerSessionRole>, override: PermissionPolicyOverride | null | undefined): PermissionPolicy;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Runtime } from "./runtime.js";
|
|
3
|
+
import { WorkerSessionRole } from "./worker-sessions.js";
|
|
4
|
+
/**
|
|
5
|
+
* The effective capabilities a worker session may exercise at the runtime boundary
|
|
6
|
+
* (ADR 0003 §Role permissions). Resolved once when the session is created and frozen
|
|
7
|
+
* into `worker_sessions.profile_snapshot_json` — a later profile edit never changes a
|
|
8
|
+
* queued or running session.
|
|
9
|
+
*
|
|
10
|
+
* Every field here is backed by a control this codebase can actually enforce from
|
|
11
|
+
* outside the worker process — a CLI tool-permission grant (`--allowedTools` /
|
|
12
|
+
* `-s read-only`) or a config-loading boundary (`--ignore-user-config`) — never a
|
|
13
|
+
* denylist over arguments to a capability (e.g. Bash) the worker already holds
|
|
14
|
+
* unrestricted, which a review round proved bypassable (absolute paths, `-C`, wrapper
|
|
15
|
+
* commands, or — for `push` — simply editing the very git config meant to restrict it,
|
|
16
|
+
* since that config lives inside the worktree the same process can already write).
|
|
17
|
+
* `push` / `openPr` are NOT modeled here for that reason: real enforcement needs those
|
|
18
|
+
* credentialed effects to move to a call the worker never makes itself (the coordinator,
|
|
19
|
+
* which already owns "the only component that publishes" for reviews) — developer→PR
|
|
20
|
+
* handoff scope (NOT-61+), not a profile toggle this ticket can honestly ship.
|
|
21
|
+
*
|
|
22
|
+
* Role defaults are the ceiling: a profile's `permissionPolicy` override may only
|
|
23
|
+
* *tighten* a capability (turn it off), never grant one the role does not have.
|
|
24
|
+
*/
|
|
25
|
+
export const PermissionPolicy = z.object({
|
|
26
|
+
/** developer only: read/write files in its own worktree — CLI tool-grant enforced. */
|
|
27
|
+
worktreeWrite: z.boolean(),
|
|
28
|
+
/** never true in v1 — the coordinator, not the reviewer, publishes the review. */
|
|
29
|
+
publishReview: z.boolean(),
|
|
30
|
+
/** call outbound-mutation deck tools (`call_service_tool`). Off for both roles in v1. */
|
|
31
|
+
outboundMutation: z.boolean(),
|
|
32
|
+
/** resolve human actions / edit workflow topology. Always off for workers. */
|
|
33
|
+
resolveHumanAction: z.boolean(),
|
|
34
|
+
});
|
|
35
|
+
/** The subset of a policy a profile may pin off. Absent field = use the role default. */
|
|
36
|
+
export const PermissionPolicyOverride = z
|
|
37
|
+
.object({
|
|
38
|
+
worktreeWrite: z.boolean().optional(),
|
|
39
|
+
outboundMutation: z.boolean().optional(),
|
|
40
|
+
})
|
|
41
|
+
.strict();
|
|
42
|
+
export const DEVELOPER_ROLE_CEILING = {
|
|
43
|
+
worktreeWrite: true,
|
|
44
|
+
publishReview: false,
|
|
45
|
+
outboundMutation: false,
|
|
46
|
+
resolveHumanAction: false,
|
|
47
|
+
};
|
|
48
|
+
export const REVIEWER_ROLE_CEILING = {
|
|
49
|
+
worktreeWrite: false,
|
|
50
|
+
publishReview: false,
|
|
51
|
+
outboundMutation: false,
|
|
52
|
+
resolveHumanAction: false,
|
|
53
|
+
};
|
|
54
|
+
export function roleCeiling(role) {
|
|
55
|
+
return role === "developer" ? DEVELOPER_ROLE_CEILING : REVIEWER_ROLE_CEILING;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Tiered reasoning / thinking effort for CLIs that expose it (Codex
|
|
59
|
+
* `model_reasoning_effort`, Claude Code `--effort`). Cursor has no separate flag —
|
|
60
|
+
* effort is only expressible inside a parameterized model id.
|
|
61
|
+
*/
|
|
62
|
+
export const ReasoningEffort = z.enum(["low", "medium", "high"]);
|
|
63
|
+
/**
|
|
64
|
+
* The immutable execution contract snapshotted onto every `worker_session` at creation
|
|
65
|
+
* (design §`agents` / §"Immutable execution-profile snapshot"). Transcripts, findings,
|
|
66
|
+
* and usage stay owned by the issue/session — completing work never writes anything back
|
|
67
|
+
* into the profile.
|
|
68
|
+
*/
|
|
69
|
+
export const ProfileSnapshot = z.object({
|
|
70
|
+
/** Schema version so an in-flight session stays reconstructable across format changes. */
|
|
71
|
+
version: z.literal(1),
|
|
72
|
+
agentId: z.string().nullable(),
|
|
73
|
+
role: WorkerSessionRole,
|
|
74
|
+
runtime: Runtime.nullable(),
|
|
75
|
+
model: z.string().nullable(),
|
|
76
|
+
/**
|
|
77
|
+
* Reasoning effort frozen with the session (NOT-81). `.default(null)` so snapshots
|
|
78
|
+
* written before this field still parse instead of invalidating the whole contract.
|
|
79
|
+
*/
|
|
80
|
+
effort: ReasoningEffort.nullable().default(null),
|
|
81
|
+
/** Serialized PhaseBudget; null = runtime default (no CLI caps). */
|
|
82
|
+
budgetJson: z.string().nullable(),
|
|
83
|
+
permissionPolicy: PermissionPolicy,
|
|
84
|
+
/** Launch-selected Deck — required for new sessions; null only on legacy in-flight rows. */
|
|
85
|
+
deckId: z.string().nullable(),
|
|
86
|
+
purpose: z.string().nullable(),
|
|
87
|
+
capturedAt: z.string(),
|
|
88
|
+
});
|
|
89
|
+
export function parseProfileSnapshot(json) {
|
|
90
|
+
if (!json?.trim())
|
|
91
|
+
return null;
|
|
92
|
+
try {
|
|
93
|
+
return ProfileSnapshot.parse(JSON.parse(json));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Serialized `string[]` helpers for the profile list columns. */
|
|
100
|
+
export function serializeStringList(list) {
|
|
101
|
+
if (!list)
|
|
102
|
+
return null;
|
|
103
|
+
const cleaned = list.map((s) => s.trim()).filter(Boolean);
|
|
104
|
+
return cleaned.length ? JSON.stringify(cleaned) : null;
|
|
105
|
+
}
|
|
106
|
+
export function parseStringList(json) {
|
|
107
|
+
if (!json?.trim())
|
|
108
|
+
return [];
|
|
109
|
+
try {
|
|
110
|
+
const parsed = JSON.parse(json);
|
|
111
|
+
return z.array(z.string()).parse(parsed);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export function serializePermissionPolicyOverride(override) {
|
|
118
|
+
if (!override)
|
|
119
|
+
return null;
|
|
120
|
+
const entries = Object.entries(override).filter(([, v]) => v !== undefined);
|
|
121
|
+
return entries.length ? JSON.stringify(Object.fromEntries(entries)) : null;
|
|
122
|
+
}
|
|
123
|
+
export function parsePermissionPolicyOverride(json) {
|
|
124
|
+
if (!json?.trim())
|
|
125
|
+
return null;
|
|
126
|
+
try {
|
|
127
|
+
return PermissionPolicyOverride.parse(JSON.parse(json));
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Apply a profile override to a role ceiling. Override can only turn capabilities off. */
|
|
134
|
+
export function resolvePermissionPolicy(role, override) {
|
|
135
|
+
const ceiling = roleCeiling(role);
|
|
136
|
+
if (!override)
|
|
137
|
+
return { ...ceiling };
|
|
138
|
+
const resolved = { ...ceiling };
|
|
139
|
+
for (const key of ["worktreeWrite", "outboundMutation"]) {
|
|
140
|
+
if (override[key] === false)
|
|
141
|
+
resolved[key] = false;
|
|
142
|
+
// override[key] === true can never raise the ceiling — ignored.
|
|
143
|
+
}
|
|
144
|
+
return resolved;
|
|
145
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { parsePermissionPolicyOverride, parseProfileSnapshot, parseStringList, resolvePermissionPolicy, serializePermissionPolicyOverride, serializeStringList, } from "./profile-snapshot.js";
|
|
4
|
+
test("serializeStringList trims, drops blanks, and returns null when empty", () => {
|
|
5
|
+
assert.equal(serializeStringList([" a ", "", "b"]), JSON.stringify(["a", "b"]));
|
|
6
|
+
assert.equal(serializeStringList([]), null);
|
|
7
|
+
assert.equal(serializeStringList(null), null);
|
|
8
|
+
assert.deepEqual(parseStringList(serializeStringList(["x", "y"])), ["x", "y"]);
|
|
9
|
+
assert.deepEqual(parseStringList("not json"), []);
|
|
10
|
+
});
|
|
11
|
+
test("resolvePermissionPolicy: override only tightens, never loosens", () => {
|
|
12
|
+
assert.equal(resolvePermissionPolicy("developer", { outboundMutation: false }).outboundMutation, false);
|
|
13
|
+
assert.equal(resolvePermissionPolicy("developer", { outboundMutation: false }).worktreeWrite, true);
|
|
14
|
+
assert.equal(resolvePermissionPolicy("reviewer", { worktreeWrite: true }).worktreeWrite, false);
|
|
15
|
+
});
|
|
16
|
+
test("permission-policy override serialization round-trips; parse rejects unknown keys", () => {
|
|
17
|
+
assert.equal(serializePermissionPolicyOverride({ worktreeWrite: false }), JSON.stringify({ worktreeWrite: false }));
|
|
18
|
+
assert.equal(serializePermissionPolicyOverride(null), null);
|
|
19
|
+
assert.deepEqual(parsePermissionPolicyOverride(JSON.stringify({ worktreeWrite: false })), {
|
|
20
|
+
worktreeWrite: false,
|
|
21
|
+
});
|
|
22
|
+
// push/openPr are not a recognized override key (never modeled — see PermissionPolicy
|
|
23
|
+
// doc comment) and resolveHumanAction is not overridable either; both are rejected.
|
|
24
|
+
assert.equal(parsePermissionPolicyOverride(JSON.stringify({ push: false })), null);
|
|
25
|
+
assert.equal(parsePermissionPolicyOverride(JSON.stringify({ resolveHumanAction: true })), null);
|
|
26
|
+
});
|
|
27
|
+
test("parseProfileSnapshot round-trips a valid snapshot and rejects junk", () => {
|
|
28
|
+
const snap = {
|
|
29
|
+
version: 1,
|
|
30
|
+
agentId: "a1",
|
|
31
|
+
role: "reviewer",
|
|
32
|
+
runtime: "claude_code",
|
|
33
|
+
model: null,
|
|
34
|
+
effort: "medium",
|
|
35
|
+
budgetJson: null,
|
|
36
|
+
permissionPolicy: resolvePermissionPolicy("reviewer", null),
|
|
37
|
+
deckId: "00000000-0000-4000-a000-000000000099",
|
|
38
|
+
purpose: null,
|
|
39
|
+
capturedAt: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
assert.deepEqual(parseProfileSnapshot(JSON.stringify(snap)), snap);
|
|
42
|
+
assert.equal(parseProfileSnapshot("{}"), null);
|
|
43
|
+
assert.equal(parseProfileSnapshot(null), null);
|
|
44
|
+
});
|
|
45
|
+
test("parseProfileSnapshot strips legacy workspace/playbook/memory fields (NOT-149)", () => {
|
|
46
|
+
const legacy = {
|
|
47
|
+
version: 1,
|
|
48
|
+
agentId: "a1",
|
|
49
|
+
role: "developer",
|
|
50
|
+
runtime: "codex_local",
|
|
51
|
+
model: "gpt-5",
|
|
52
|
+
budgetJson: null,
|
|
53
|
+
permissionPolicy: resolvePermissionPolicy("developer", null),
|
|
54
|
+
deckId: null,
|
|
55
|
+
workspaceRoot: "/repo",
|
|
56
|
+
playbookIds: ["pb_x"],
|
|
57
|
+
externalMemoryRefs: ["vault://x"],
|
|
58
|
+
purpose: null,
|
|
59
|
+
capturedAt: new Date().toISOString(),
|
|
60
|
+
};
|
|
61
|
+
const parsed = parseProfileSnapshot(JSON.stringify(legacy));
|
|
62
|
+
assert.ok(parsed);
|
|
63
|
+
assert.equal(parsed.effort, null);
|
|
64
|
+
assert.equal("workspaceRoot" in parsed, false);
|
|
65
|
+
assert.equal("playbookIds" in parsed, false);
|
|
66
|
+
assert.equal("externalMemoryRefs" in parsed, false);
|
|
67
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { WorkflowInstance } from "./workflow.js";
|
|
3
|
+
export declare const QueueEntryState: z.ZodEnum<["queued", "admitted", "removed"]>;
|
|
4
|
+
export type QueueEntryState = z.infer<typeof QueueEntryState>;
|
|
5
|
+
export declare const QueueEntry: z.ZodObject<{
|
|
6
|
+
id: z.ZodString;
|
|
7
|
+
issueId: z.ZodString;
|
|
8
|
+
position: z.ZodNumber;
|
|
9
|
+
enqueuedAt: z.ZodString;
|
|
10
|
+
state: z.ZodEnum<["queued", "admitted", "removed"]>;
|
|
11
|
+
waitReason: z.ZodNullable<z.ZodString>;
|
|
12
|
+
waitReasonAt: z.ZodNullable<z.ZodString>;
|
|
13
|
+
}, "strip", z.ZodTypeAny, {
|
|
14
|
+
id: string;
|
|
15
|
+
issueId: string;
|
|
16
|
+
position: number;
|
|
17
|
+
enqueuedAt: string;
|
|
18
|
+
state: "queued" | "admitted" | "removed";
|
|
19
|
+
waitReason: string | null;
|
|
20
|
+
waitReasonAt: string | null;
|
|
21
|
+
}, {
|
|
22
|
+
id: string;
|
|
23
|
+
issueId: string;
|
|
24
|
+
position: number;
|
|
25
|
+
enqueuedAt: string;
|
|
26
|
+
state: "queued" | "admitted" | "removed";
|
|
27
|
+
waitReason: string | null;
|
|
28
|
+
waitReasonAt: string | null;
|
|
29
|
+
}>;
|
|
30
|
+
export type QueueEntry = z.infer<typeof QueueEntry>;
|
|
31
|
+
/** Enriched row for GET /api/queue — includes issue title for UI/CLI. */
|
|
32
|
+
export declare const QueueEntryView: z.ZodObject<{
|
|
33
|
+
id: z.ZodString;
|
|
34
|
+
issueId: z.ZodString;
|
|
35
|
+
position: z.ZodNumber;
|
|
36
|
+
enqueuedAt: z.ZodString;
|
|
37
|
+
state: z.ZodEnum<["queued", "admitted", "removed"]>;
|
|
38
|
+
waitReason: z.ZodNullable<z.ZodString>;
|
|
39
|
+
waitReasonAt: z.ZodNullable<z.ZodString>;
|
|
40
|
+
} & {
|
|
41
|
+
title: z.ZodOptional<z.ZodString>;
|
|
42
|
+
issueStatus: z.ZodOptional<z.ZodString>;
|
|
43
|
+
}, "strip", z.ZodTypeAny, {
|
|
44
|
+
id: string;
|
|
45
|
+
issueId: string;
|
|
46
|
+
position: number;
|
|
47
|
+
enqueuedAt: string;
|
|
48
|
+
state: "queued" | "admitted" | "removed";
|
|
49
|
+
waitReason: string | null;
|
|
50
|
+
waitReasonAt: string | null;
|
|
51
|
+
title?: string | undefined;
|
|
52
|
+
issueStatus?: string | undefined;
|
|
53
|
+
}, {
|
|
54
|
+
id: string;
|
|
55
|
+
issueId: string;
|
|
56
|
+
position: number;
|
|
57
|
+
enqueuedAt: string;
|
|
58
|
+
state: "queued" | "admitted" | "removed";
|
|
59
|
+
waitReason: string | null;
|
|
60
|
+
waitReasonAt: string | null;
|
|
61
|
+
title?: string | undefined;
|
|
62
|
+
issueStatus?: string | undefined;
|
|
63
|
+
}>;
|
|
64
|
+
export type QueueEntryView = z.infer<typeof QueueEntryView>;
|
|
65
|
+
export declare const EnqueueIssueInput: z.ZodObject<{
|
|
66
|
+
issueId: z.ZodString;
|
|
67
|
+
}, "strip", z.ZodTypeAny, {
|
|
68
|
+
issueId: string;
|
|
69
|
+
}, {
|
|
70
|
+
issueId: string;
|
|
71
|
+
}>;
|
|
72
|
+
export type EnqueueIssueInput = z.infer<typeof EnqueueIssueInput>;
|
|
73
|
+
/**
|
|
74
|
+
* NOT-118 `POST /api/issues/:id/start` response. Start has no queue bypass: it moves the
|
|
75
|
+
* issue to the front and admits it when a slot is free, otherwise it waits at the top with
|
|
76
|
+
* a reason. `workItem` is the round-1 developer item the admitted start enqueued.
|
|
77
|
+
*/
|
|
78
|
+
export type StartIssueResponse = {
|
|
79
|
+
state: "admitted";
|
|
80
|
+
instance: WorkflowInstance;
|
|
81
|
+
workItem: {
|
|
82
|
+
id: string;
|
|
83
|
+
kind: string;
|
|
84
|
+
};
|
|
85
|
+
} | {
|
|
86
|
+
state: "queued";
|
|
87
|
+
position: number;
|
|
88
|
+
waitReason: string | null;
|
|
89
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const QueueEntryState = z.enum(["queued", "admitted", "removed"]);
|
|
3
|
+
export const QueueEntry = z.object({
|
|
4
|
+
id: z.string().uuid(),
|
|
5
|
+
issueId: z.string().uuid(),
|
|
6
|
+
position: z.number().int(),
|
|
7
|
+
enqueuedAt: z.string(),
|
|
8
|
+
state: QueueEntryState,
|
|
9
|
+
waitReason: z.string().nullable(),
|
|
10
|
+
waitReasonAt: z.string().nullable(),
|
|
11
|
+
});
|
|
12
|
+
/** Enriched row for GET /api/queue — includes issue title for UI/CLI. */
|
|
13
|
+
export const QueueEntryView = QueueEntry.extend({
|
|
14
|
+
title: z.string().optional(),
|
|
15
|
+
issueStatus: z.string().optional(),
|
|
16
|
+
});
|
|
17
|
+
export const EnqueueIssueInput = z.object({
|
|
18
|
+
issueId: z.string().uuid(),
|
|
19
|
+
});
|
|
@@ -13,7 +13,7 @@ export declare const ResultQaContent: z.ZodObject<{
|
|
|
13
13
|
answeredAt: z.ZodOptional<z.ZodString>;
|
|
14
14
|
error: z.ZodOptional<z.ZodString>;
|
|
15
15
|
}, "strip", z.ZodTypeAny, {
|
|
16
|
-
status: "
|
|
16
|
+
status: "failed" | "pending" | "answered";
|
|
17
17
|
question: string;
|
|
18
18
|
exchangeId: string;
|
|
19
19
|
sessionResumed: boolean;
|
|
@@ -22,7 +22,7 @@ export declare const ResultQaContent: z.ZodObject<{
|
|
|
22
22
|
answer?: string | undefined;
|
|
23
23
|
error?: string | undefined;
|
|
24
24
|
}, {
|
|
25
|
-
status: "
|
|
25
|
+
status: "failed" | "pending" | "answered";
|
|
26
26
|
question: string;
|
|
27
27
|
exchangeId: string;
|
|
28
28
|
sessionResumed: boolean;
|