@astrosheep/keiyaku 2.8.4 → 2.9.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/README.md +35 -15
- package/build/.tsbuildinfo +1 -1
- package/build/agents/{call-terms_v2.js → call-terms.js} +12 -8
- package/build/agents/harness/control-types.js +1 -0
- package/build/agents/harness/execution-handle.js +23 -3
- package/build/agents/harness/index.js +6 -607
- package/build/agents/harness/outcome.js +141 -0
- package/build/agents/harness/projection.js +310 -0
- package/build/agents/harness/router.js +201 -0
- package/build/agents/harness/runtime.js +2 -1
- package/build/agents/{launch-snapshot_v2.js → launch-snapshot/model.js} +69 -74
- package/build/agents/launch-snapshot/resolve.js +72 -0
- package/build/agents/opencode-sdk.js +24 -3
- package/build/agents/providers/{claude-agent-sdk.js → claude-agent-sdk/adapter.js} +46 -368
- package/build/agents/providers/claude-agent-sdk/events.js +218 -0
- package/build/agents/providers/claude-agent-sdk/session.js +158 -0
- package/build/agents/providers/codex-app-server/adapter.js +347 -0
- package/build/agents/providers/codex-app-server/events.js +352 -0
- package/build/agents/providers/codex-app-server/session.js +266 -0
- package/build/agents/providers/codex-sdk.js +23 -5
- package/build/agents/providers/opencode-sdk/adapter.js +344 -0
- package/build/agents/providers/opencode-sdk/events.js +354 -0
- package/build/agents/providers/opencode-sdk/session.js +180 -0
- package/build/agents/providers/pi/adapter.js +269 -0
- package/build/agents/providers/pi/checkpoint.js +41 -0
- package/build/agents/providers/pi/events.js +282 -0
- package/build/agents/{selector_v2.js → selector.js} +8 -6
- package/build/cli/commands/akuma/akuma/handler.js +4 -0
- package/build/cli/commands/akuma/akuma/meta.js +16 -0
- package/build/cli/commands/akuma/catalog.js +8 -0
- package/build/cli/commands/akuma/list/handler.js +15 -0
- package/build/cli/commands/akuma/list/meta-ls.js +9 -0
- package/build/cli/commands/akuma/view/handler.js +34 -0
- package/build/cli/commands/akuma/view/meta.js +8 -0
- package/build/cli/commands/akuma.js +1 -1
- package/build/cli/commands/contract/amend/handler.js +10 -0
- package/build/cli/commands/contract/amend/meta.js +12 -0
- package/build/cli/commands/contract/arc/handler.js +9 -0
- package/build/cli/commands/contract/arc/meta.js +26 -0
- package/build/cli/commands/contract/bind/handler.js +23 -0
- package/build/cli/commands/contract/bind/meta.js +30 -0
- package/build/cli/commands/contract/catalog.js +16 -0
- package/build/cli/commands/contract/forfeit/handler.js +13 -0
- package/build/cli/commands/contract/forfeit/meta.js +12 -0
- package/build/cli/commands/contract/log/handler.js +12 -0
- package/build/cli/commands/contract/log/meta.js +12 -0
- package/build/cli/commands/contract/petition/handler.js +26 -0
- package/build/cli/commands/contract/petition/meta.js +23 -0
- package/build/cli/commands/contract/renew/handler.js +10 -0
- package/build/cli/commands/contract/renew/meta.js +17 -0
- package/build/cli/commands/projection/call/handler.js +39 -0
- package/build/cli/commands/projection/call/meta.js +9 -0
- package/build/cli/commands/projection/catalog.js +14 -0
- package/build/cli/commands/projection/kill/handler.js +35 -0
- package/build/cli/commands/projection/kill/meta.js +11 -0
- package/build/cli/commands/projection/revive/handler.js +53 -0
- package/build/cli/commands/projection/revive/meta.js +9 -0
- package/build/cli/commands/projection/status/handler.js +23 -0
- package/build/cli/commands/projection/status/meta.js +9 -0
- package/build/cli/commands/projection/tell/handler.js +34 -0
- package/build/cli/commands/projection/tell/meta.js +9 -0
- package/build/cli/commands/projection/wait/handler.js +21 -0
- package/build/cli/commands/projection/wait/meta.js +11 -0
- package/build/cli/commands/registry.js +100 -0
- package/build/cli/commands/shared.js +71 -0
- package/build/cli/commands/skills/catalog.js +6 -0
- package/build/cli/commands/skills/install/handler.js +10 -0
- package/build/cli/commands/skills/install/meta.js +9 -0
- package/build/cli/commands/skills/skills/handler.js +4 -0
- package/build/cli/commands/skills/skills/meta.js +10 -0
- package/build/cli/commands/system/catalog.js +8 -0
- package/build/cli/commands/system/completion/handler.js +10 -0
- package/build/cli/commands/system/completion/meta.js +9 -0
- package/build/cli/commands/system/dump-env/handler.js +3 -0
- package/build/cli/commands/system/dump-env/meta.js +10 -0
- package/build/cli/commands/system/guide/handler.js +4 -0
- package/build/cli/commands/system/guide/meta.js +9 -0
- package/build/cli/commands/task/add/handler.js +21 -0
- package/build/cli/commands/task/add/meta.js +8 -0
- package/build/cli/commands/task/catalog.js +22 -0
- package/build/cli/commands/task/doctor/handler.js +4 -0
- package/build/cli/commands/task/doctor/meta.js +8 -0
- package/build/cli/commands/task/done/handler.js +17 -0
- package/build/cli/commands/task/done/meta.js +8 -0
- package/build/cli/commands/task/drop/handler.js +6 -0
- package/build/cli/commands/task/drop/meta.js +8 -0
- package/build/cli/commands/task/ls/handler.js +13 -0
- package/build/cli/commands/task/ls/meta.js +8 -0
- package/build/cli/commands/task/shared.js +131 -0
- package/build/cli/commands/task/start/handler.js +6 -0
- package/build/cli/commands/task/start/meta.js +8 -0
- package/build/cli/commands/task/stop/handler.js +17 -0
- package/build/cli/commands/task/stop/meta.js +8 -0
- package/build/cli/commands/task/task/handler.js +5 -0
- package/build/cli/commands/task/task/meta.js +11 -0
- package/build/cli/commands/task/update/handler.js +18 -0
- package/build/cli/commands/task/update/meta.js +8 -0
- package/build/cli/commands/task/view/handler.js +14 -0
- package/build/cli/commands/task/view/meta.js +8 -0
- package/build/cli/completion.js +26 -27
- package/build/cli/flags.js +79 -2
- package/build/cli/help.js +27 -49
- package/build/cli/index.js +121 -374
- package/build/cli/output-stream-error-policy.js +17 -0
- package/build/cli/parse.js +159 -72
- package/build/cli/projection-address.js +5 -0
- package/build/cli/render/arc.js +1 -1
- package/build/cli/render/errors.js +4 -3
- package/build/cli/render/format.js +14 -0
- package/build/cli/render/kanshi.js +106 -0
- package/build/cli/render/petition.js +24 -1
- package/build/cli/render/projection-activity.js +5 -1
- package/build/cli/render/shared.js +2 -2
- package/build/cli/render/status.js +79 -13
- package/build/cli/render/task.js +113 -0
- package/build/cli/render/wait.js +23 -3
- package/build/cli/types.js +1 -1
- package/build/config/{akuma-loader_v2.js → akuma-loader.js} +2 -2
- package/build/config/env.js +9 -4
- package/build/config/settings/disease.js +26 -0
- package/build/config/settings/knobs.js +8 -0
- package/build/config/settings/loader.js +244 -0
- package/build/config/settings/schema.js +276 -0
- package/build/core/addressing.js +1 -1
- package/build/core/amend.js +3 -3
- package/build/core/arc.js +5 -4
- package/build/core/bind-workspace.js +123 -0
- package/build/core/bind.js +75 -155
- package/build/core/call/call.js +224 -0
- package/build/core/call/context.js +238 -0
- package/build/core/call/execution.js +217 -0
- package/build/core/call/prompt.js +42 -0
- package/build/core/call/reference.js +24 -0
- package/build/core/call-persist.js +1 -1
- package/build/core/claim-delivery.js +247 -0
- package/build/core/claim.js +39 -277
- package/build/core/context.js +2 -1
- package/build/core/{render.js → contract-view.js} +12 -140
- package/build/core/draft.js +107 -9
- package/build/core/entry.js +9 -1
- package/build/core/execution-coordinate.js +4 -20
- package/build/core/execution-pact.js +16 -31
- package/build/core/forfeit.js +35 -22
- package/build/core/hints.js +2 -2
- package/build/core/log.js +2 -2
- package/build/core/petition-claim-gates.js +8 -6
- package/build/core/petition-preview.js +97 -0
- package/build/core/petition-run.js +3 -2
- package/build/core/petition.js +39 -26
- package/build/core/projection/generation/database.js +306 -0
- package/build/core/projection/generation/identity.js +84 -0
- package/build/core/projection/generation/model.js +201 -0
- package/build/core/projection/generation/store.js +73 -0
- package/build/core/projection/generation/transitions.js +258 -0
- package/build/core/projection/heart.js +0 -307
- package/build/core/projection/tell/database.js +109 -0
- package/build/core/projection/tell/model.js +97 -0
- package/build/core/{projection-tells.js → projection/tell/store.js} +3 -158
- package/build/core/projection-activity.js +23 -2
- package/build/core/projection-core.js +2 -1
- package/build/core/projection-execution-observer.js +259 -0
- package/build/core/projection-generation-continuation.js +1 -1
- package/build/core/projection-generation-execution.js +90 -20
- package/build/core/projection-generation-launcher.js +1 -1
- package/build/core/projection-generation-runner.js +198 -4
- package/build/core/projection-identity.js +1 -1
- package/build/core/projection-kill.js +50 -17
- package/build/core/projection-life-observer.js +1 -1
- package/build/core/projection-life-protocol.js +10 -0
- package/build/core/projection-mint.js +1 -1
- package/build/core/projection-status.js +45 -32
- package/build/core/projection-wait.js +12 -2
- package/build/core/projection-wake.js +5 -2
- package/build/core/{queue_v2.js → queue.js} +2 -1
- package/build/core/registry.js +1 -1
- package/build/core/renew.js +7 -7
- package/build/core/response-artifact-catalog-sqlite.js +112 -0
- package/build/core/response-artifact-catalog.js +7 -0
- package/build/core/seal.js +44 -16
- package/build/core/status/board.js +286 -0
- package/build/core/status/drift.js +99 -0
- package/build/core/status/lifecycle.js +84 -0
- package/build/core/status/reconciliation.js +131 -0
- package/build/core/stored-agent-event.js +28 -4
- package/build/core/target-ref.js +10 -0
- package/build/core/task/board.js +175 -0
- package/build/core/task/commands.js +211 -0
- package/build/core/task/document.js +189 -0
- package/build/core/task/model.js +1 -0
- package/build/core/task/settlement-git.js +207 -0
- package/build/core/task/settlement-policy.js +82 -0
- package/build/core/task/source-board.js +84 -0
- package/build/core/task/source-model.js +1 -0
- package/build/core/task/validation.js +44 -0
- package/build/core/task-contract.js +224 -0
- package/build/core/task-doctor.js +14 -0
- package/build/core/task-git-actor.js +38 -0
- package/build/core/task-git-runtime.js +34 -0
- package/build/core/task-git-store.js +375 -0
- package/build/core/task-repository.js +1 -0
- package/build/core/task-store-repository.js +86 -0
- package/build/core/task-store.js +1 -0
- package/build/core/task-worktree.js +51 -0
- package/build/core/task.js +4 -0
- package/build/core/transcripts.js +138 -56
- package/build/core/validation.js +17 -0
- package/build/core/verdict.js +2 -2
- package/build/core/worktree-path.js +24 -6
- package/build/flow-error.js +3 -2
- package/build/generated/version.js +1 -1
- package/build/git/commits.js +3 -0
- package/build/git/diff/parsers.js +0 -94
- package/build/index.js +2 -0
- package/build/keiyaku-constants.js +2 -0
- package/build/keiyaku.js +4 -3
- package/package.json +3 -2
- package/skills/keiyaku/SKILL.md +1 -1
- package/skills/keiyaku-akuma/SKILL.md +11 -6
- package/build/agents/harness/pump.js +0 -158
- package/build/agents/providers/codex-app-server.js +0 -864
- package/build/agents/providers/opencode-sdk.js +0 -363
- package/build/agents/providers/pi.js +0 -557
- package/build/config/settings.js +0 -533
- package/build/core/call.js +0 -686
- package/build/core/projection/generation.js +0 -412
- package/build/core/projection/identity.js +0 -81
- package/build/core/projection/mint.js +0 -174
- package/build/core/projection-generation-store.js +0 -707
- package/build/core/status.js +0 -539
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { requireHealthy } from "./task/board.js";
|
|
2
|
+
import { parseTaskDocument, serializeTaskDocument } from "./task/document.js";
|
|
3
|
+
import { buildTaskSourceBoard } from "./task/source-board.js";
|
|
4
|
+
function taskBoardFromSourceBoard(sourceBoard) {
|
|
5
|
+
const tasks = sourceBoard.tasks.map((task) => ({
|
|
6
|
+
id: task.id,
|
|
7
|
+
title: task.title,
|
|
8
|
+
state: task.state,
|
|
9
|
+
pri: task.pri,
|
|
10
|
+
needs: [...task.needs],
|
|
11
|
+
parent: task.parent,
|
|
12
|
+
from: [...task.from],
|
|
13
|
+
createdAt: task.createdAt,
|
|
14
|
+
creator: task.creator,
|
|
15
|
+
...(task.startedAt === undefined ? {} : { startedAt: task.startedAt }),
|
|
16
|
+
...(task.contractId === undefined ? {} : { contractId: task.contractId }),
|
|
17
|
+
body: task.body,
|
|
18
|
+
blocks: [...task.blocks],
|
|
19
|
+
blocked: task.blocked,
|
|
20
|
+
releasedNeeds: [...task.releasedNeeds],
|
|
21
|
+
}));
|
|
22
|
+
return {
|
|
23
|
+
tasks,
|
|
24
|
+
diseases: sourceBoard.diseases.map((disease) => ({
|
|
25
|
+
code: disease.code,
|
|
26
|
+
message: disease.message,
|
|
27
|
+
...(disease.id === undefined ? {} : { id: disease.id }),
|
|
28
|
+
...(disease.relatedIds === undefined ? {} : { relatedIds: [...disease.relatedIds] }),
|
|
29
|
+
})),
|
|
30
|
+
byId: new Map(tasks.map((task) => [task.id, task])),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function prepareTaskDocumentReplacements(sources, replacements) {
|
|
34
|
+
const serialized = replacements.map((document) => ({
|
|
35
|
+
id: document.id,
|
|
36
|
+
text: serializeTaskDocument(document),
|
|
37
|
+
}));
|
|
38
|
+
const replacementIds = new Set(serialized.map((replacement) => replacement.id));
|
|
39
|
+
const paths = new Map();
|
|
40
|
+
const retained = sources.filter((source) => {
|
|
41
|
+
try {
|
|
42
|
+
const id = parseTaskDocument(source).id;
|
|
43
|
+
paths.set(id, source.path);
|
|
44
|
+
return !replacementIds.has(id);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Malformed sources remain quarantined and visible to the resulting board.
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
const replacementSources = serialized.map((replacement) => ({
|
|
52
|
+
path: paths.get(replacement.id) ?? `${replacement.id}.md`,
|
|
53
|
+
text: replacement.text,
|
|
54
|
+
}));
|
|
55
|
+
const resultingSources = [...retained, ...replacementSources];
|
|
56
|
+
const board = taskBoardFromSourceBoard(buildTaskSourceBoard(resultingSources));
|
|
57
|
+
requireHealthy(board);
|
|
58
|
+
return {
|
|
59
|
+
sources: resultingSources,
|
|
60
|
+
writes: serialized,
|
|
61
|
+
board,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function mutationPlan(sources, mutation) {
|
|
65
|
+
const prepared = prepareTaskDocumentReplacements(sources, mutation.replacements);
|
|
66
|
+
return {
|
|
67
|
+
writes: prepared.writes,
|
|
68
|
+
value: mutation.select(prepared.board),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export function taskRepositoryFromStore(store) {
|
|
72
|
+
return {
|
|
73
|
+
read: async () => taskBoardFromSourceBoard(buildTaskSourceBoard(await store.read())),
|
|
74
|
+
mutate: async (apply) => store.mutate(async (sources) => mutationPlan(sources, await apply(taskBoardFromSourceBoard(buildTaskSourceBoard(sources))))),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function taskRepositoryFromLockedBoard(transaction) {
|
|
78
|
+
return {
|
|
79
|
+
read: async () => taskBoardFromSourceBoard(buildTaskSourceBoard(transaction.sources)),
|
|
80
|
+
mutate: async (apply) => {
|
|
81
|
+
const plan = mutationPlan(transaction.sources, await apply(taskBoardFromSourceBoard(buildTaskSourceBoard(transaction.sources))));
|
|
82
|
+
await transaction.replace(plan.writes);
|
|
83
|
+
return plan.value;
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { FlowError } from "../flow-error.js";
|
|
2
|
+
function documentFacts(task) {
|
|
3
|
+
return JSON.stringify({
|
|
4
|
+
id: task.id,
|
|
5
|
+
title: task.title,
|
|
6
|
+
state: task.state,
|
|
7
|
+
pri: task.pri,
|
|
8
|
+
needs: [...task.needs],
|
|
9
|
+
parent: task.parent,
|
|
10
|
+
from: [...task.from],
|
|
11
|
+
createdAt: task.createdAt,
|
|
12
|
+
creator: task.creator,
|
|
13
|
+
...(task.startedAt === undefined ? {} : { startedAt: task.startedAt }),
|
|
14
|
+
...(task.contractId === undefined ? {} : { contractId: task.contractId }),
|
|
15
|
+
body: task.body,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function isNeedsAppend(prior, next) {
|
|
19
|
+
return next.needs.length > prior.needs.length
|
|
20
|
+
&& prior.needs.every((need, index) => next.needs[index] === need)
|
|
21
|
+
&& new Set(next.needs).size === next.needs.length
|
|
22
|
+
&& documentFacts(prior) === documentFacts({ ...next, needs: prior.needs });
|
|
23
|
+
}
|
|
24
|
+
export function validateTaskWorktreeMutation(before, replacements, after, contractId) {
|
|
25
|
+
const existing = new Map(before.tasks.map((task) => [task.id, task]));
|
|
26
|
+
const bound = before.tasks.filter((task) => task.contractId === contractId);
|
|
27
|
+
for (const replacement of replacements) {
|
|
28
|
+
const prior = existing.get(replacement.id);
|
|
29
|
+
if (!prior)
|
|
30
|
+
continue;
|
|
31
|
+
const next = after.byId.get(replacement.id);
|
|
32
|
+
if (bound.length === 1 && prior.id === bound[0].id && next && isNeedsAppend(prior, next))
|
|
33
|
+
continue;
|
|
34
|
+
throw new FlowError("UNSUPPORTED_POLICY", `task ${replacement.id} is not yours; this checkout is bound to contract ${contractId}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function withTaskWorktreePolicy(repository, contractId) {
|
|
38
|
+
return {
|
|
39
|
+
read: repository.read.bind(repository),
|
|
40
|
+
mutate: async (apply) => repository.mutate(async (before) => {
|
|
41
|
+
const mutation = await apply(before);
|
|
42
|
+
return {
|
|
43
|
+
replacements: mutation.replacements,
|
|
44
|
+
select(after) {
|
|
45
|
+
validateTaskWorktreeMutation(before, mutation.replacements, after, contractId);
|
|
46
|
+
return mutation.select(after);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { parseTaskDocument, serializeTaskDocument, } from "./task/document.js";
|
|
2
|
+
export { resolveTaskReadId, } from "./task/board.js";
|
|
3
|
+
export { buildTaskBoard } from "./task/source-board.js";
|
|
4
|
+
export { readTaskBoard, createTask, updateTask, startTask, stopTask, doneTask, dropTask, selectSoleInProgress, rollbackOrphanTaskPointers, } from "./task/commands.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { parseResolvedAkumaLaunchSnapshot, } from "../agents/launch-
|
|
4
|
+
import { parseResolvedAkumaLaunchSnapshot, } from "../agents/launch-snapshot/model.js";
|
|
5
5
|
import { resolvePersistedSubagentSession } from "../agents/session.js";
|
|
6
6
|
import { isErrnoException } from "../errno.js";
|
|
7
7
|
import { FlowError, requireText } from "../flow-error.js";
|
|
@@ -13,6 +13,8 @@ import { parseExecutionCoordinates } from "./execution-coordinate.js";
|
|
|
13
13
|
import { createUlid } from "./ids.js";
|
|
14
14
|
import { parseToAST } from "./markdown/parser.js";
|
|
15
15
|
import { isResponseArtifactId, RESPONSE_ARTIFACT_ID_RE } from "./response-artifact-id.js";
|
|
16
|
+
import { getResponseArtifactCatalog, } from "./response-artifact-catalog.js";
|
|
17
|
+
import { assertExactFields, assertNonBlankString, isPlainRecord, } from "./validation.js";
|
|
16
18
|
export { isResponseArtifactId, RESPONSE_ARTIFACT_ID_RE } from "./response-artifact-id.js";
|
|
17
19
|
const RESPONSE_HISTORY_SLUG_MAX_LENGTH = 24;
|
|
18
20
|
const RESPONSE_HISTORY_SESSION_KEY = "session";
|
|
@@ -30,43 +32,30 @@ const RESUMABLE_RESPONSE_HEADINGS = Object.values(RESPONSE_ARTIFACT_HEADINGS);
|
|
|
30
32
|
function artifactError(locator, field, reason) {
|
|
31
33
|
return new FlowError("INVALID_RESPONSE_PATH", `Invalid response artifact '${locator}': field '${field}' ${reason}.`);
|
|
32
34
|
}
|
|
33
|
-
function
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
function assertExactFields(value, required, locator, prefix) {
|
|
37
|
-
for (const field of required) {
|
|
38
|
-
if (!(field in value))
|
|
39
|
-
throw artifactError(locator, `${prefix}.${field}`, "is missing");
|
|
40
|
-
}
|
|
41
|
-
const allowed = new Set(required);
|
|
42
|
-
const unknown = Object.keys(value).filter((field) => !allowed.has(field)).sort()[0];
|
|
43
|
-
if (unknown !== undefined)
|
|
44
|
-
throw artifactError(locator, `${prefix}.${unknown}`, "is unknown");
|
|
45
|
-
}
|
|
46
|
-
function nonBlank(value, locator, field) {
|
|
47
|
-
if (typeof value !== "string" || value.trim() === "")
|
|
48
|
-
throw artifactError(locator, field, "must be a non-blank string");
|
|
35
|
+
function artifactInvalid(locator) {
|
|
36
|
+
return (field, reason) => artifactError(locator, field, reason);
|
|
49
37
|
}
|
|
50
38
|
/** The sole durable provenance parser. */
|
|
51
39
|
export function parseResponseArtifactProvenance(value, locator) {
|
|
52
|
-
|
|
40
|
+
const invalid = artifactInvalid(locator);
|
|
41
|
+
if (!isPlainRecord(value))
|
|
53
42
|
throw artifactError(locator, "provenance", "must be an object");
|
|
54
|
-
assertExactFields(value, ["artifactId", "authority", "binding", "evidence"],
|
|
55
|
-
|
|
43
|
+
assertExactFields(value, ["artifactId", "authority", "binding", "evidence"], [], "provenance", invalid);
|
|
44
|
+
assertNonBlankString(value.artifactId, "provenance.artifactId", invalid);
|
|
56
45
|
if (!RESPONSE_ARTIFACT_ID_RE.test(value.artifactId)) {
|
|
57
46
|
throw artifactError(locator, "provenance.artifactId", "must be rsp_ followed by a ULID");
|
|
58
47
|
}
|
|
59
48
|
const { authority, binding } = parseExecutionCoordinates(value.authority, value.binding, (field, reason) => artifactError(locator, `provenance.${field}`, reason));
|
|
60
|
-
if (!
|
|
49
|
+
if (!isPlainRecord(value.evidence))
|
|
61
50
|
throw artifactError(locator, "provenance.evidence", "must be an object");
|
|
62
51
|
if (authority.kind === "repository") {
|
|
63
|
-
assertExactFields(value.evidence, ["branch", "commit"],
|
|
64
|
-
|
|
65
|
-
|
|
52
|
+
assertExactFields(value.evidence, ["branch", "commit"], [], "provenance.evidence", invalid);
|
|
53
|
+
assertNonBlankString(value.evidence.branch, "provenance.evidence.branch", invalid);
|
|
54
|
+
assertNonBlankString(value.evidence.commit, "provenance.evidence.commit", invalid);
|
|
66
55
|
return { artifactId: value.artifactId, authority, binding, evidence: { branch: value.evidence.branch, commit: value.evidence.commit } };
|
|
67
56
|
}
|
|
68
|
-
assertExactFields(value.evidence, ["workdir"],
|
|
69
|
-
|
|
57
|
+
assertExactFields(value.evidence, ["workdir"], [], "provenance.evidence", invalid);
|
|
58
|
+
assertNonBlankString(value.evidence.workdir, "provenance.evidence.workdir", invalid);
|
|
70
59
|
if (!path.isAbsolute(value.evidence.workdir)) {
|
|
71
60
|
throw artifactError(locator, "provenance.evidence.workdir", "must be an absolute path");
|
|
72
61
|
}
|
|
@@ -130,6 +119,15 @@ export async function persistResponseHistory(input) {
|
|
|
130
119
|
const responsePath = path.join(historyDir, `${fileStamp}_${input.tool.toUpperCase()}_${slug(input.title ?? "", input.tool)}.md`);
|
|
131
120
|
await fs.mkdir(historyDir, { recursive: true });
|
|
132
121
|
await fs.writeFile(responsePath, buildMarkdown(input, iso, provenance), "utf8");
|
|
122
|
+
try {
|
|
123
|
+
getResponseArtifactCatalog()?.register({
|
|
124
|
+
id: artifactId,
|
|
125
|
+
stableRoot: await fs.realpath(input.cwd),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// The catalog is a disposable read model; the persisted artifact remains truth.
|
|
130
|
+
}
|
|
133
131
|
return { artifactId, responsePath: path.relative(input.cwd, responsePath) };
|
|
134
132
|
}
|
|
135
133
|
function detectTool(document) {
|
|
@@ -206,43 +204,30 @@ export async function readResponseArtifact(input) {
|
|
|
206
204
|
}
|
|
207
205
|
}
|
|
208
206
|
const subagentName = frontmatter.subagent_name?.trim() || undefined;
|
|
209
|
-
const storageRoot = path.resolve(path.dirname(absolutePath), "..", "..");
|
|
207
|
+
const storageRoot = await fs.realpath(path.resolve(path.dirname(absolutePath), "..", ".."));
|
|
210
208
|
return { artifactId: provenance.artifactId, responsePath: path.relative(input.cwd, absolutePath), absolutePath, storageRoot, provenance, tool, ...(session ? { session } : {}), ...(launchSnapshot ? { launchSnapshot } : {}), ...(subagentName ? { subagentName } : {}) };
|
|
211
209
|
}
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
};
|
|
216
|
-
let identity;
|
|
217
|
-
try {
|
|
218
|
-
identity = requireText("artifactId", input.artifactId);
|
|
219
|
-
}
|
|
220
|
-
catch (error) {
|
|
221
|
-
return carry(error);
|
|
222
|
-
}
|
|
223
|
-
if (!isResponseArtifactId(identity)) {
|
|
224
|
-
return carry(new FlowError("INVALID_RESPONSE_PATH", `${identity} is not a response artifact handle; revive expects an artifact id`));
|
|
225
|
-
}
|
|
226
|
-
const historyDir = path.join(input.cwd, RESPONSE_HISTORY_DIR);
|
|
210
|
+
async function findResponseArtifactAtRoot(input) {
|
|
211
|
+
const physicalHistoryDir = path.join(input.stableRoot, RESPONSE_HISTORY_DIR);
|
|
212
|
+
const historyDir = path.join(input.displayRoot ?? input.stableRoot, RESPONSE_HISTORY_DIR);
|
|
227
213
|
let names;
|
|
228
214
|
try {
|
|
229
|
-
names = await fs.readdir(
|
|
215
|
+
names = await fs.readdir(physicalHistoryDir);
|
|
230
216
|
}
|
|
231
217
|
catch (error) {
|
|
232
|
-
if (isErrnoException(error) && error.code === "ENOENT")
|
|
233
|
-
return
|
|
234
|
-
|
|
235
|
-
return carry(error);
|
|
218
|
+
if (isErrnoException(error) && error.code === "ENOENT")
|
|
219
|
+
return { kind: "missing" };
|
|
220
|
+
return input.carry(error);
|
|
236
221
|
}
|
|
237
222
|
let match;
|
|
238
223
|
for (const name of names.filter((candidate) => candidate.endsWith(".md")).sort()) {
|
|
239
224
|
const responsePath = path.join(RESPONSE_HISTORY_DIR, name);
|
|
240
225
|
let document;
|
|
241
226
|
try {
|
|
242
|
-
document = await fs.readFile(path.join(input.
|
|
227
|
+
document = await fs.readFile(path.join(input.stableRoot, responsePath), "utf8");
|
|
243
228
|
}
|
|
244
229
|
catch (error) {
|
|
245
|
-
return carry(error);
|
|
230
|
+
return input.carry(error);
|
|
246
231
|
}
|
|
247
232
|
const encoded = parseToAST(document).frontmatter?.entries?.[RESPONSE_HISTORY_PROVENANCE_KEY];
|
|
248
233
|
if (encoded === undefined)
|
|
@@ -254,26 +239,123 @@ export async function locateResponseArtifact(input) {
|
|
|
254
239
|
catch {
|
|
255
240
|
continue;
|
|
256
241
|
}
|
|
257
|
-
if (!
|
|
242
|
+
if (!isPlainRecord(indexed) || indexed.artifactId !== input.artifactId)
|
|
258
243
|
continue;
|
|
259
244
|
let parsed;
|
|
260
245
|
try {
|
|
261
|
-
parsed = await readResponseArtifact({ cwd: input.
|
|
246
|
+
parsed = await readResponseArtifact({ cwd: input.stableRoot, responsePath });
|
|
262
247
|
}
|
|
263
248
|
catch (error) {
|
|
264
|
-
return carry(error);
|
|
249
|
+
return input.carry(error);
|
|
265
250
|
}
|
|
266
251
|
if (!parsed)
|
|
267
252
|
continue;
|
|
268
253
|
if (match) {
|
|
269
|
-
return carry(new FlowError("INVALID_RESPONSE_PATH", `Response artifact identity '${
|
|
254
|
+
return input.carry(new FlowError("INVALID_RESPONSE_PATH", `Response artifact identity '${input.artifactId}' is duplicated in ${historyDir}.`));
|
|
270
255
|
}
|
|
271
256
|
match = parsed;
|
|
272
257
|
}
|
|
273
|
-
|
|
274
|
-
|
|
258
|
+
return match ? { kind: "found", artifact: match } : { kind: "missing" };
|
|
259
|
+
}
|
|
260
|
+
function observeCatalogArtifact(catalog, artifact) {
|
|
261
|
+
try {
|
|
262
|
+
catalog?.touch({ id: artifact.artifactId, stableRoot: artifact.storageRoot });
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
// Artifact observation remains authoritative when the disposable catalog is unavailable.
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
export async function reconcileResponseArtifactCatalog(stableRoot) {
|
|
269
|
+
const catalog = getResponseArtifactCatalog();
|
|
270
|
+
if (!catalog)
|
|
271
|
+
return;
|
|
272
|
+
const physicalRoot = await fs.realpath(stableRoot);
|
|
273
|
+
const historyDir = path.join(physicalRoot, RESPONSE_HISTORY_DIR);
|
|
274
|
+
let names;
|
|
275
|
+
try {
|
|
276
|
+
names = await fs.readdir(historyDir);
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
if (isErrnoException(error) && error.code === "ENOENT")
|
|
280
|
+
return;
|
|
281
|
+
throw error;
|
|
275
282
|
}
|
|
276
|
-
|
|
283
|
+
for (const name of names.filter((candidate) => candidate.endsWith(".md")).sort()) {
|
|
284
|
+
try {
|
|
285
|
+
const artifact = await readResponseArtifact({
|
|
286
|
+
cwd: physicalRoot,
|
|
287
|
+
responsePath: path.join(RESPONSE_HISTORY_DIR, name),
|
|
288
|
+
});
|
|
289
|
+
if (artifact)
|
|
290
|
+
catalog.register({ id: artifact.artifactId, stableRoot: artifact.storageRoot });
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
// Reconciliation observes valid local artifacts only; it never repairs artifact truth.
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
export async function locateResponseArtifact(input) {
|
|
298
|
+
const carry = (error) => {
|
|
299
|
+
throw attachAddressCarrier(error, input.attempt);
|
|
300
|
+
};
|
|
301
|
+
let identity;
|
|
302
|
+
try {
|
|
303
|
+
identity = requireText("artifactId", input.artifactId);
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
return carry(error);
|
|
307
|
+
}
|
|
308
|
+
if (!isResponseArtifactId(identity)) {
|
|
309
|
+
return carry(new FlowError("INVALID_RESPONSE_PATH", `${identity} is not a response artifact handle; revive expects an artifact id`));
|
|
310
|
+
}
|
|
311
|
+
let physicalLocalRoot;
|
|
312
|
+
try {
|
|
313
|
+
physicalLocalRoot = await fs.realpath(input.cwd);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
if (!isErrnoException(error) || error.code !== "ENOENT")
|
|
317
|
+
return carry(error);
|
|
318
|
+
physicalLocalRoot = path.resolve(input.cwd);
|
|
319
|
+
}
|
|
320
|
+
const local = await findResponseArtifactAtRoot({
|
|
321
|
+
stableRoot: physicalLocalRoot,
|
|
322
|
+
displayRoot: input.cwd,
|
|
323
|
+
artifactId: identity,
|
|
324
|
+
carry,
|
|
325
|
+
});
|
|
326
|
+
const catalog = getResponseArtifactCatalog();
|
|
327
|
+
if (local.kind === "found") {
|
|
328
|
+
observeCatalogArtifact(catalog, local.artifact);
|
|
329
|
+
return local.artifact;
|
|
330
|
+
}
|
|
331
|
+
let cataloged;
|
|
332
|
+
try {
|
|
333
|
+
cataloged = catalog?.lookup(identity);
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
cataloged = undefined;
|
|
337
|
+
}
|
|
338
|
+
if (cataloged) {
|
|
339
|
+
const catalogedRoot = cataloged.stableRoot;
|
|
340
|
+
const remote = await findResponseArtifactAtRoot({
|
|
341
|
+
stableRoot: catalogedRoot,
|
|
342
|
+
artifactId: identity,
|
|
343
|
+
carry,
|
|
344
|
+
});
|
|
345
|
+
if (remote.kind === "found") {
|
|
346
|
+
observeCatalogArtifact(catalog, remote.artifact);
|
|
347
|
+
return remote.artifact;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
catalog?.removeDead({ id: identity, stableRoot: catalogedRoot });
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
// Dead-row cleanup is best-effort; the physical lookup result owns the error.
|
|
354
|
+
}
|
|
355
|
+
return carry(new FlowError("INVALID_RESPONSE_PATH", `${identity} not found at cataloged root ${catalogedRoot}`));
|
|
356
|
+
}
|
|
357
|
+
const historyDir = path.join(input.cwd, RESPONSE_HISTORY_DIR);
|
|
358
|
+
return carry(new FlowError("INVALID_RESPONSE_PATH", `Response artifact '${identity}' was not found in ${historyDir}.`));
|
|
277
359
|
}
|
|
278
360
|
export function requireResumableArtifact(artifact) {
|
|
279
361
|
if (!artifact.session)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function isPlainRecord(value) {
|
|
2
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
export function assertExactFields(value, required, optional, prefix, invalid) {
|
|
5
|
+
for (const field of required) {
|
|
6
|
+
if (!(field in value))
|
|
7
|
+
throw invalid(prefix ? `${prefix}.${field}` : field, "is missing");
|
|
8
|
+
}
|
|
9
|
+
const allowed = new Set([...required, ...optional]);
|
|
10
|
+
const unknown = Object.keys(value).filter((field) => !allowed.has(field)).sort()[0];
|
|
11
|
+
if (unknown !== undefined)
|
|
12
|
+
throw invalid(prefix ? `${prefix}.${unknown}` : unknown, "is unknown");
|
|
13
|
+
}
|
|
14
|
+
export function assertNonBlankString(value, field, invalid) {
|
|
15
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
16
|
+
throw invalid(field, "must be a non-blank string");
|
|
17
|
+
}
|
package/build/core/verdict.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { FlowError } from "../flow-error.js";
|
|
2
2
|
import { claimContract } from "./claim.js";
|
|
3
3
|
import { activeLedgerRef, appendWithRetry, readLedger } from "./ledger.js";
|
|
4
|
-
import { assertClaimQueueHead, queueMembership, readQueueReadModel } from "./
|
|
5
|
-
import { deriveContractState } from "./status.js";
|
|
4
|
+
import { assertClaimQueueHead, queueMembership, readQueueReadModel } from "./queue.js";
|
|
5
|
+
import { deriveContractState } from "./status/lifecycle.js";
|
|
6
6
|
function latestVerdict(entries) {
|
|
7
7
|
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
8
8
|
const entry = entries[index];
|
|
@@ -4,7 +4,7 @@ import { FlowError } from "../flow-error.js";
|
|
|
4
4
|
import { createGit, wrapGitError } from "../git/core.js";
|
|
5
5
|
import { readLedger } from "./ledger.js";
|
|
6
6
|
import { listContractIds } from "./ledger.js";
|
|
7
|
-
import { deriveContractState, isTerminalState } from "./status.js";
|
|
7
|
+
import { deriveContractState, isTerminalState } from "./status/lifecycle.js";
|
|
8
8
|
export function findBindEntry(entries) {
|
|
9
9
|
return entries.find((entry) => entry.kind === "bind");
|
|
10
10
|
}
|
|
@@ -94,11 +94,11 @@ export async function findContractWorktreePath(cwd, contractId) {
|
|
|
94
94
|
return (await findContractWorktree(cwd, contractId)).path;
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
97
|
-
* Resolve the
|
|
98
|
-
*
|
|
99
|
-
* worktrees
|
|
97
|
+
* Resolve the contract id for an engine-owned bound worktree containing cwd.
|
|
98
|
+
* Matches the canonical bind place path and worktree workspace only; ledger
|
|
99
|
+
* terminal state is optional so residual worktrees after cleanup still bind.
|
|
100
100
|
*/
|
|
101
|
-
|
|
101
|
+
async function contractIdForEngineOwnedWorktree(cwd, options) {
|
|
102
102
|
const stableRoot = await stableRepoRoot(cwd);
|
|
103
103
|
const canonicalCwd = await fs.realpath(cwd);
|
|
104
104
|
for (const worktree of await listWorktrees(stableRoot)) {
|
|
@@ -107,7 +107,9 @@ export async function contractPresentedByWorktree(cwd) {
|
|
|
107
107
|
continue;
|
|
108
108
|
const contractId = worktree.branch.slice(branchPrefix.length);
|
|
109
109
|
const ledger = await readLedger(stableRoot, contractId);
|
|
110
|
-
if (!ledger
|
|
110
|
+
if (!ledger)
|
|
111
|
+
continue;
|
|
112
|
+
if (options.requireActive && isTerminalState(deriveContractState(ledger.entries)))
|
|
111
113
|
continue;
|
|
112
114
|
const bind = findBindEntry(ledger.entries);
|
|
113
115
|
if (!bind || bind.data.workspace !== "worktree")
|
|
@@ -128,6 +130,22 @@ export async function contractPresentedByWorktree(cwd) {
|
|
|
128
130
|
}
|
|
129
131
|
return undefined;
|
|
130
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Resolve the active commission presented by an engine-owned contract
|
|
135
|
+
* worktree containing cwd. Ordinary repository directories and arbitrary
|
|
136
|
+
* worktrees never present a commission address.
|
|
137
|
+
*/
|
|
138
|
+
export async function contractPresentedByWorktree(cwd) {
|
|
139
|
+
return contractIdForEngineOwnedWorktree(cwd, { requireActive: true });
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Resolve the contract bound by an engine-owned worktree containing cwd,
|
|
143
|
+
* including when the ledger is already terminal. Task CLI contract context
|
|
144
|
+
* uses this residual binding; call/addressing keep active-only presentation.
|
|
145
|
+
*/
|
|
146
|
+
export async function contractBoundByEngineWorktree(cwd) {
|
|
147
|
+
return contractIdForEngineOwnedWorktree(cwd, { requireActive: false });
|
|
148
|
+
}
|
|
131
149
|
export async function cleanupContractWorkspace(cwd, contractId) {
|
|
132
150
|
const stableRoot = await stableRepoRoot(cwd);
|
|
133
151
|
const git = createGit(stableRoot);
|
package/build/flow-error.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ACTOR_IDENTITY,
|
|
1
|
+
import { ACTOR_IDENTITY, SETTINGS_PATH_SEGMENTS } from "./keiyaku-constants.js";
|
|
2
|
+
const SETTINGS_DISPLAY_PATH = SETTINGS_PATH_SEGMENTS.join("/");
|
|
2
3
|
const DEFAULT_HINT_CONTEXT = {
|
|
3
4
|
bindToolName: "bind",
|
|
4
5
|
arcToolName: "arc",
|
|
@@ -63,7 +64,7 @@ function resolveFlowErrorHint(code, context) {
|
|
|
63
64
|
case "INVALID_CLOSE_INTENT":
|
|
64
65
|
return "Invalid close intent. Valid values are `CLAIM` and `FORFEIT`.";
|
|
65
66
|
case "INVALID_SETTINGS":
|
|
66
|
-
return `Fix invalid configuration in \`${
|
|
67
|
+
return `Fix invalid configuration in \`${SETTINGS_DISPLAY_PATH}\` and retry.`;
|
|
67
68
|
case "INVALID_EFFORT_OVERRIDE":
|
|
68
69
|
return "Choose an effort value declared by the selected Akuma's effortOptions.";
|
|
69
70
|
case "CONCURRENT_MODIFICATION":
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by scripts/generate-version.mjs
|
|
2
|
-
export const VERSION = "2.8.
|
|
2
|
+
export const VERSION = "2.8.5";
|
package/build/git/commits.js
CHANGED
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
import { Buffer } from "node:buffer";
|
|
2
|
-
const DIFF_GIT_HEADER_PREFIX = "diff --git ";
|
|
3
1
|
const DIFF_TRUNCATED_MARKER = "...[truncated]";
|
|
4
|
-
const GIT_QUOTED_PATH_ESCAPES = {
|
|
5
|
-
'"': 34,
|
|
6
|
-
"\\": 92,
|
|
7
|
-
a: 7,
|
|
8
|
-
b: 8,
|
|
9
|
-
f: 12,
|
|
10
|
-
n: 10,
|
|
11
|
-
r: 13,
|
|
12
|
-
t: 9,
|
|
13
|
-
v: 11,
|
|
14
|
-
};
|
|
15
2
|
export function splitDiffByFile(content) {
|
|
16
3
|
const sections = [];
|
|
17
4
|
let current = [];
|
|
@@ -94,87 +81,6 @@ export function isMissingBaseRevisionError(err) {
|
|
|
94
81
|
text.includes("unknown revision or path not in the working tree") ||
|
|
95
82
|
text.includes("ambiguous argument"));
|
|
96
83
|
}
|
|
97
|
-
function unescapeQuotedDiffPath(value) {
|
|
98
|
-
const bytes = [];
|
|
99
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
100
|
-
const char = value[index];
|
|
101
|
-
if (char !== "\\") {
|
|
102
|
-
bytes.push(...Buffer.from(char ?? "", "utf8"));
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
const escaped = value[index + 1];
|
|
106
|
-
if (escaped === undefined) {
|
|
107
|
-
bytes.push(92);
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
if (escaped >= "0" && escaped <= "7") {
|
|
111
|
-
let octal = escaped;
|
|
112
|
-
let cursor = index + 2;
|
|
113
|
-
while (cursor < value.length && octal.length < 3) {
|
|
114
|
-
const digit = value[cursor];
|
|
115
|
-
if (digit === undefined || digit < "0" || digit > "7")
|
|
116
|
-
break;
|
|
117
|
-
octal += digit;
|
|
118
|
-
cursor += 1;
|
|
119
|
-
}
|
|
120
|
-
bytes.push(Number.parseInt(octal, 8));
|
|
121
|
-
index = cursor - 1;
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
const decoded = GIT_QUOTED_PATH_ESCAPES[escaped];
|
|
125
|
-
if (decoded !== undefined) {
|
|
126
|
-
bytes.push(decoded);
|
|
127
|
-
index += 1;
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
bytes.push(...Buffer.from(escaped, "utf8"));
|
|
131
|
-
index += 1;
|
|
132
|
-
}
|
|
133
|
-
return Buffer.from(bytes).toString("utf8");
|
|
134
|
-
}
|
|
135
|
-
function readDiffHeaderToken(line, startIndex) {
|
|
136
|
-
let index = startIndex;
|
|
137
|
-
while (index < line.length && line[index] === " ") {
|
|
138
|
-
index += 1;
|
|
139
|
-
}
|
|
140
|
-
if (index >= line.length)
|
|
141
|
-
return null;
|
|
142
|
-
if (line[index] !== '"') {
|
|
143
|
-
let endIndex = index;
|
|
144
|
-
while (endIndex < line.length && line[endIndex] !== " ") {
|
|
145
|
-
endIndex += 1;
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
value: line.slice(index, endIndex),
|
|
149
|
-
nextIndex: endIndex,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
let endIndex = index + 1;
|
|
153
|
-
let escaped = false;
|
|
154
|
-
while (endIndex < line.length) {
|
|
155
|
-
const char = line[endIndex];
|
|
156
|
-
if (!escaped && char === '"') {
|
|
157
|
-
return {
|
|
158
|
-
value: unescapeQuotedDiffPath(line.slice(index + 1, endIndex)),
|
|
159
|
-
nextIndex: endIndex + 1,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
escaped = !escaped && char === "\\";
|
|
163
|
-
endIndex += 1;
|
|
164
|
-
}
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
export function parseDiffPathFromHeader(diffGitLine) {
|
|
168
|
-
if (!diffGitLine.startsWith(DIFF_GIT_HEADER_PREFIX))
|
|
169
|
-
return null;
|
|
170
|
-
const aPart = readDiffHeaderToken(diffGitLine, DIFF_GIT_HEADER_PREFIX.length);
|
|
171
|
-
if (!aPart)
|
|
172
|
-
return null;
|
|
173
|
-
const bPart = readDiffHeaderToken(diffGitLine, aPart.nextIndex)?.value ?? "";
|
|
174
|
-
if (!bPart.startsWith("b/"))
|
|
175
|
-
return null;
|
|
176
|
-
return bPart.slice(2);
|
|
177
|
-
}
|
|
178
84
|
export function totalJoinedLength(blocks) {
|
|
179
85
|
if (blocks.length === 0)
|
|
180
86
|
return 0;
|
package/build/index.js
CHANGED
|
@@ -6,7 +6,9 @@ import { ENV_KEYS } from "./config/env-keys.js";
|
|
|
6
6
|
import { logErrorWithCause } from "./telemetry/logger.js";
|
|
7
7
|
import { runProjectionGenerationRuntime } from "./core/projection-generation-runtime.js";
|
|
8
8
|
import { renderCliHelp, runCliCommand } from "./cli/index.js";
|
|
9
|
+
import { installCliOutputStreamErrorPolicy } from "./cli/output-stream-error-policy.js";
|
|
9
10
|
import { assertSubagentCliCommandAllowed } from "./cli/subagent-guard.js";
|
|
11
|
+
installCliOutputStreamErrorPolicy(process.stdout, process.stderr);
|
|
10
12
|
const packageVersion = VERSION;
|
|
11
13
|
function findCliCwd(args) {
|
|
12
14
|
for (let index = 0; index < args.length; index += 1) {
|