@sortedyuan/maybecode 0.3.2 → 0.5.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 +166 -55
- package/README.zh-CN.md +267 -0
- package/dist/application.d.ts +40 -34
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +290 -170
- package/dist/application.js.map +1 -1
- package/dist/args.d.ts +2 -2
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +50 -27
- package/dist/args.js.map +1 -1
- package/dist/bin.js +82 -71
- package/dist/bin.js.map +4 -4
- package/dist/cli/chunk-3IORPKBL.js +3572 -0
- package/dist/cli/chunk-3IORPKBL.js.map +7 -0
- package/dist/cli/{chunk-LVTRXPDS.js → chunk-4B2Z6KW7.js} +1547 -1536
- package/dist/cli/chunk-4B2Z6KW7.js.map +7 -0
- package/dist/cli/{chunk-JT6VBKOM.js → chunk-4WG6JIJJ.js} +127 -33
- package/dist/cli/{chunk-JT6VBKOM.js.map → chunk-4WG6JIJJ.js.map} +3 -3
- package/dist/cli/{chunk-K5JWPNSY.js → chunk-5SLWALZK.js} +3521 -16164
- package/dist/cli/chunk-5SLWALZK.js.map +7 -0
- package/dist/cli/chunk-MNBYXL5Y.js +132 -0
- package/dist/cli/chunk-MNBYXL5Y.js.map +7 -0
- package/dist/cli/{chunk-F4XRPLUM.js → chunk-SLWLUEHQ.js} +226 -86
- package/dist/cli/chunk-SLWLUEHQ.js.map +7 -0
- package/dist/cli/chunk-UQDZ55LQ.js +199 -0
- package/dist/cli/chunk-UQDZ55LQ.js.map +7 -0
- package/dist/cli/{chunk-7ISOHFS7.js → chunk-WMSG5ZTR.js} +717 -388
- package/dist/cli/chunk-WMSG5ZTR.js.map +7 -0
- package/dist/cli/chunk-WVIRS76R.js +4057 -0
- package/dist/cli/chunk-WVIRS76R.js.map +7 -0
- package/dist/cli/{chunk-HOU5ITUK.js → chunk-XTEMRAQW.js} +1 -1
- package/dist/cli/chunk-XTEMRAQW.js.map +7 -0
- package/dist/cli/chunk-Z3BOPBVU.js +91 -0
- package/dist/cli/chunk-Z3BOPBVU.js.map +7 -0
- package/dist/cli/{configured-WHY3GGAJ.js → configured-ACNM6TRQ.js} +10 -9
- package/dist/cli/{file-type-R2T7TBHI.js → file-type-HLHPV7WC.js} +817 -13
- package/dist/cli/file-type-HLHPV7WC.js.map +7 -0
- package/dist/cli/mcp-auth-XD5U7WCV.js +74 -0
- package/dist/cli/mcp-auth-XD5U7WCV.js.map +7 -0
- package/dist/cli/onboarding-WQWCPYEO.js +530 -0
- package/dist/cli/onboarding-WQWCPYEO.js.map +7 -0
- package/dist/cli/onboarding-check-HRRI5OJ7.js +32 -0
- package/dist/cli/onboarding-check-HRRI5OJ7.js.map +7 -0
- package/dist/cli/{retained-tui-VJ3VX4R3.js → retained-tui-QF5VMYLC.js} +788 -332
- package/dist/cli/retained-tui-QF5VMYLC.js.map +7 -0
- package/dist/cli/{sixel-LWTCM76G.js → sixel-ISZ4FZ3U.js} +37 -37
- package/dist/cli/{sixel-LWTCM76G.js.map → sixel-ISZ4FZ3U.js.map} +1 -1
- package/dist/cli/{team-T7G7PPNN.js → team-O2I5BXHM.js} +7 -6
- package/dist/cli/{team-v2-JGBBQ7FR.js → team-v2-YYO2G7DJ.js} +816 -390
- package/dist/cli/team-v2-YYO2G7DJ.js.map +7 -0
- package/dist/cli/{web-server-FOMNPY7Q.js → web-server-SXMH6FGA.js} +55 -19
- package/dist/cli/web-server-SXMH6FGA.js.map +7 -0
- package/dist/cli/{web-ui-COB6PKC5.js → web-ui-UWQUFE4W.js} +7 -6
- package/dist/config-schema.json +337 -0
- package/dist/configured.d.ts +18 -11
- package/dist/configured.d.ts.map +1 -1
- package/dist/configured.js +213 -390
- package/dist/configured.js.map +1 -1
- package/dist/controller.d.ts +19 -18
- package/dist/controller.d.ts.map +1 -1
- package/dist/delegation-commands.d.ts +1 -1
- package/dist/delegation-commands.d.ts.map +1 -1
- package/dist/delegation-commands.js +8 -8
- package/dist/delegation-commands.js.map +1 -1
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -1
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +8 -6
- package/dist/events.d.ts.map +1 -1
- package/dist/fork-picker.d.ts.map +1 -1
- package/dist/fork-picker.js +19 -5
- package/dist/fork-picker.js.map +1 -1
- package/dist/git-request.d.ts +6 -6
- package/dist/git-request.d.ts.map +1 -1
- package/dist/git-request.js +28 -15
- package/dist/git-request.js.map +1 -1
- package/dist/goal-commands.d.ts +1 -1
- package/dist/goal-commands.d.ts.map +1 -1
- package/dist/goal-commands.js +12 -5
- package/dist/goal-commands.js.map +1 -1
- package/dist/index.d.ts +5 -37
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -36
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +2 -7
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +7 -15
- package/dist/instructions.js.map +1 -1
- package/dist/keymap.d.ts +2 -2
- package/dist/keymap.d.ts.map +1 -1
- package/dist/keymap.js +2 -2
- package/dist/keymap.js.map +1 -1
- package/dist/maybecode-config.schema.json +1765 -0
- package/dist/mcp-auth.d.ts +1 -1
- package/dist/mcp-auth.d.ts.map +1 -1
- package/dist/mcp-auth.js +11 -8
- package/dist/mcp-auth.js.map +1 -1
- package/dist/mcp-commands.d.ts.map +1 -1
- package/dist/mcp-commands.js +18 -4
- package/dist/mcp-commands.js.map +1 -1
- package/dist/mcp-interaction-ui.d.ts.map +1 -1
- package/dist/mcp-interaction-ui.js +11 -4
- package/dist/mcp-interaction-ui.js.map +1 -1
- package/dist/media-history.d.ts +1 -1
- package/dist/media-history.d.ts.map +1 -1
- package/dist/media-history.js +8 -2
- package/dist/media-history.js.map +1 -1
- package/dist/model-picker.d.ts.map +1 -1
- package/dist/model-picker.js +10 -11
- package/dist/model-picker.js.map +1 -1
- package/dist/model-transition.d.ts +11 -0
- package/dist/model-transition.d.ts.map +1 -0
- package/dist/model-transition.js +48 -0
- package/dist/model-transition.js.map +1 -0
- package/dist/model.d.ts +2 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +8 -4
- package/dist/model.js.map +1 -1
- package/dist/onboarding-check.d.ts +3 -0
- package/dist/onboarding-check.d.ts.map +1 -0
- package/dist/onboarding-check.js +22 -0
- package/dist/onboarding-check.js.map +1 -0
- package/dist/onboarding.d.ts +6 -0
- package/dist/onboarding.d.ts.map +1 -0
- package/dist/onboarding.js +463 -0
- package/dist/onboarding.js.map +1 -0
- package/dist/plugins/application.d.ts +6 -4
- package/dist/plugins/application.d.ts.map +1 -1
- package/dist/plugins/application.js +106 -52
- package/dist/plugins/application.js.map +1 -1
- package/dist/plugins/instructions.d.ts +1 -1
- package/dist/plugins/instructions.d.ts.map +1 -1
- package/dist/plugins/instructions.js +24 -11
- package/dist/plugins/instructions.js.map +1 -1
- package/dist/plugins/resources.d.ts +2 -2
- package/dist/plugins/resources.d.ts.map +1 -1
- package/dist/plugins/resources.js +22 -12
- package/dist/plugins/resources.js.map +1 -1
- package/dist/policy.d.ts +1 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +59 -11
- package/dist/policy.js.map +1 -1
- package/dist/run.d.ts +2 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +38 -33
- package/dist/run.js.map +1 -1
- package/dist/runtime-instructions.d.ts +1 -1
- package/dist/runtime-instructions.d.ts.map +1 -1
- package/dist/runtime-instructions.js +15 -7
- package/dist/runtime-instructions.js.map +1 -1
- package/dist/session-navigation.d.ts +6 -0
- package/dist/session-navigation.d.ts.map +1 -0
- package/dist/session-navigation.js +64 -0
- package/dist/session-navigation.js.map +1 -0
- package/dist/session-picker.d.ts +1 -1
- package/dist/session-picker.d.ts.map +1 -1
- package/dist/session-picker.js +23 -32
- package/dist/session-picker.js.map +1 -1
- package/dist/settings.d.ts +718 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +14 -0
- package/dist/settings.js.map +1 -0
- package/dist/skills.d.ts +1 -1
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +10 -12
- package/dist/skills.js.map +1 -1
- package/dist/slash-commands.d.ts +2 -2
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +149 -67
- package/dist/slash-commands.js.map +1 -1
- package/dist/team-execution.d.ts +10 -0
- package/dist/team-execution.d.ts.map +1 -0
- package/dist/team-execution.js +29 -0
- package/dist/team-execution.js.map +1 -0
- package/dist/team-patches.d.ts.map +1 -1
- package/dist/team-patches.js +149 -52
- package/dist/team-patches.js.map +1 -1
- package/dist/team-plan.d.ts +2 -2
- package/dist/team-plan.d.ts.map +1 -1
- package/dist/team-plan.js +115 -34
- package/dist/team-plan.js.map +1 -1
- package/dist/team-recovery.d.ts +1 -1
- package/dist/team-recovery.d.ts.map +1 -1
- package/dist/team-recovery.js +40 -10
- package/dist/team-recovery.js.map +1 -1
- package/dist/team-v2.d.ts.map +1 -1
- package/dist/team-v2.js +335 -104
- package/dist/team-v2.js.map +1 -1
- package/dist/team-verification.d.ts +4 -2
- package/dist/team-verification.d.ts.map +1 -1
- package/dist/team-verification.js +300 -97
- package/dist/team-verification.js.map +1 -1
- package/dist/team.d.ts +2 -2
- package/dist/team.d.ts.map +1 -1
- package/dist/team.js +172 -62
- package/dist/team.js.map +1 -1
- package/dist/terminal-web.d.ts +1 -0
- package/dist/terminal-web.d.ts.map +1 -1
- package/dist/terminal-web.js +28 -10
- package/dist/terminal-web.js.map +1 -1
- package/dist/ui/actions.d.ts.map +1 -1
- package/dist/ui/actions.js.map +1 -1
- package/dist/ui/retained-tui.d.ts.map +1 -1
- package/dist/ui/retained-tui.js +231 -173
- package/dist/ui/retained-tui.js.map +1 -1
- package/dist/ui/{prototype-view.d.ts → terminal-view.d.ts} +7 -7
- package/dist/ui/terminal-view.d.ts.map +1 -0
- package/dist/ui/{prototype-view.js → terminal-view.js} +185 -98
- package/dist/ui/terminal-view.js.map +1 -0
- package/dist/web-commands.d.ts.map +1 -1
- package/dist/web-commands.js +160 -57
- package/dist/web-commands.js.map +1 -1
- package/dist/web-extension.d.ts.map +1 -1
- package/dist/web-extension.js +14 -4
- package/dist/web-extension.js.map +1 -1
- package/dist/web-server.d.ts.map +1 -1
- package/dist/web-server.js +2 -1
- package/dist/web-server.js.map +1 -1
- package/dist/web-ui.d.ts.map +1 -1
- package/dist/web-ui.js +167 -40
- package/dist/web-ui.js.map +1 -1
- package/dist/workspace-configuration.d.ts +7 -0
- package/dist/workspace-configuration.d.ts.map +1 -0
- package/dist/workspace-configuration.js +134 -0
- package/dist/workspace-configuration.js.map +1 -0
- package/dist/workspace-state.d.ts +20 -0
- package/dist/workspace-state.d.ts.map +1 -0
- package/dist/workspace-state.js +2 -0
- package/dist/workspace-state.js.map +1 -0
- package/dist/workspace.d.ts +26 -43
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +261 -323
- package/dist/workspace.js.map +1 -1
- package/package.json +21 -31
- package/dist/cli/chunk-7ISOHFS7.js.map +0 -7
- package/dist/cli/chunk-F4XRPLUM.js.map +0 -7
- package/dist/cli/chunk-H7SNBFO4.js +0 -5522
- package/dist/cli/chunk-H7SNBFO4.js.map +0 -7
- package/dist/cli/chunk-HOU5ITUK.js.map +0 -7
- package/dist/cli/chunk-K5JWPNSY.js.map +0 -7
- package/dist/cli/chunk-LVTRXPDS.js.map +0 -7
- package/dist/cli/chunk-VAZMCNBB.js +0 -686
- package/dist/cli/chunk-VAZMCNBB.js.map +0 -7
- package/dist/cli/chunk-WOFEI42C.js +0 -7820
- package/dist/cli/chunk-WOFEI42C.js.map +0 -7
- package/dist/cli/file-type-R2T7TBHI.js.map +0 -7
- package/dist/cli/mcp-auth-UNRHSIKJ.js +0 -63
- package/dist/cli/mcp-auth-UNRHSIKJ.js.map +0 -7
- package/dist/cli/retained-tui-VJ3VX4R3.js.map +0 -7
- package/dist/cli/team-v2-JGBBQ7FR.js.map +0 -7
- package/dist/cli/tui-SA74DKMA.js +0 -1705
- package/dist/cli/tui-SA74DKMA.js.map +0 -7
- package/dist/cli/web-server-FOMNPY7Q.js.map +0 -7
- package/dist/delegation.d.ts +0 -2
- package/dist/delegation.d.ts.map +0 -1
- package/dist/delegation.js +0 -2
- package/dist/delegation.js.map +0 -1
- package/dist/history-memory.d.ts +0 -2
- package/dist/history-memory.d.ts.map +0 -1
- package/dist/history-memory.js +0 -2
- package/dist/history-memory.js.map +0 -1
- package/dist/subagent-budget.d.ts +0 -2
- package/dist/subagent-budget.d.ts.map +0 -1
- package/dist/subagent-budget.js +0 -2
- package/dist/subagent-budget.js.map +0 -1
- package/dist/subagent-files.d.ts +0 -2
- package/dist/subagent-files.d.ts.map +0 -1
- package/dist/subagent-files.js +0 -2
- package/dist/subagent-files.js.map +0 -1
- package/dist/subagent-host.d.ts +0 -2
- package/dist/subagent-host.d.ts.map +0 -1
- package/dist/subagent-host.js +0 -2
- package/dist/subagent-host.js.map +0 -1
- package/dist/subagent-instructions.d.ts +0 -2
- package/dist/subagent-instructions.d.ts.map +0 -1
- package/dist/subagent-instructions.js +0 -2
- package/dist/subagent-instructions.js.map +0 -1
- package/dist/subagents.d.ts +0 -2
- package/dist/subagents.d.ts.map +0 -1
- package/dist/subagents.js +0 -2
- package/dist/subagents.js.map +0 -1
- package/dist/summarizer.d.ts +0 -2
- package/dist/summarizer.d.ts.map +0 -1
- package/dist/summarizer.js +0 -2
- package/dist/summarizer.js.map +0 -1
- package/dist/tui.d.ts +0 -7
- package/dist/tui.d.ts.map +0 -1
- package/dist/tui.js +0 -1109
- package/dist/tui.js.map +0 -1
- package/dist/ui/prototype-view.d.ts.map +0 -1
- package/dist/ui/prototype-view.js.map +0 -1
- /package/dist/cli/{configured-WHY3GGAJ.js.map → configured-ACNM6TRQ.js.map} +0 -0
- /package/dist/cli/{team-T7G7PPNN.js.map → team-O2I5BXHM.js.map} +0 -0
- /package/dist/cli/{web-ui-COB6PKC5.js.map → web-ui-UWQUFE4W.js.map} +0 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
+
TeamExecutionEnvironments,
|
|
3
4
|
createListTool
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-SLWLUEHQ.js";
|
|
5
6
|
import {
|
|
6
7
|
createMaybeCodeModel,
|
|
7
8
|
getDefaultMaybeCodeDataDirectory,
|
|
8
|
-
|
|
9
|
+
readMaybeCodeSettings,
|
|
10
|
+
resolveMaybeCodeRecording,
|
|
9
11
|
selectMaybeCodeModel
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-3IORPKBL.js";
|
|
13
|
+
import "./chunk-MNBYXL5Y.js";
|
|
14
|
+
import "./chunk-WVIRS76R.js";
|
|
13
15
|
import {
|
|
14
|
-
AsyncStateSerializer,
|
|
15
16
|
CoordinationRuntime,
|
|
16
17
|
FileArtifactStore,
|
|
17
18
|
FileCoordinationStore,
|
|
@@ -19,49 +20,60 @@ import {
|
|
|
19
20
|
MaybeCodeUsageError,
|
|
20
21
|
TaskWorkspaceManager,
|
|
21
22
|
createApplicationAgent,
|
|
22
|
-
|
|
23
|
-
createReadTool,
|
|
24
|
-
createWriteTool,
|
|
25
|
-
defineAgent,
|
|
23
|
+
require_lib,
|
|
26
24
|
sharedBudgetTotals
|
|
27
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-5SLWALZK.js";
|
|
26
|
+
import "./chunk-UQDZ55LQ.js";
|
|
28
27
|
import "./chunk-BSBNNF34.js";
|
|
29
28
|
import {
|
|
30
|
-
__name
|
|
29
|
+
__name,
|
|
30
|
+
__toESM
|
|
31
31
|
} from "./chunk-E732GDOI.js";
|
|
32
32
|
|
|
33
33
|
// apps/maybecode/src/team-v2.ts
|
|
34
34
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
35
|
-
import { mkdir as mkdir3, open as open5, readFile, realpath as realpath3,
|
|
35
|
+
import { mkdir as mkdir3, open as open5, readFile, realpath as realpath3, stat } from "node:fs/promises";
|
|
36
36
|
import { dirname as dirname2, isAbsolute as isAbsolute3, join as join4, relative as relative3, resolve as resolve3, sep as sep3 } from "node:path";
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
|
|
37
|
+
import { defineAgent } from "@sortedyuan/may";
|
|
38
|
+
import {
|
|
39
|
+
createEditTool,
|
|
40
|
+
createReadTool,
|
|
41
|
+
createWriteTool,
|
|
42
|
+
createGlobTool,
|
|
43
|
+
createGrepTool,
|
|
44
|
+
createFetchTool
|
|
45
|
+
} from "@sortedyuan/coding-tools";
|
|
46
|
+
import { loadMayConfig } from "@sortedyuan/may/config";
|
|
47
|
+
var import_write_file_atomic = __toESM(require_lib(), 1);
|
|
48
|
+
import { resolveRunBudget as resolveRunBudget2 } from "@sortedyuan/may/core";
|
|
49
|
+
import { FileSessionStore as FileSessionStore2 } from "@sortedyuan/may/session/file-store";
|
|
50
|
+
import { hasErrorCode as hasErrorCode3, withFileAccess } from "@sortedyuan/may/storage";
|
|
40
51
|
|
|
41
52
|
// apps/maybecode/src/team-plan.ts
|
|
42
53
|
import { open as open2 } from "node:fs/promises";
|
|
43
|
-
import { resolveRunBudget } from "@sortedyuan/core";
|
|
54
|
+
import { omitUndefined, resolveRunBudget } from "@sortedyuan/may/core";
|
|
44
55
|
|
|
45
56
|
// apps/maybecode/src/team-verification.ts
|
|
46
|
-
import { acquireFileLock,
|
|
57
|
+
import { acquireFileLock, hasErrorCode } from "@sortedyuan/may/storage";
|
|
47
58
|
import { createHash } from "node:crypto";
|
|
48
|
-
import { spawn } from "node:child_process";
|
|
49
59
|
import { constants } from "node:fs";
|
|
50
60
|
import { lstat, mkdir, open, readdir, realpath } from "node:fs/promises";
|
|
51
61
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
52
62
|
import { isDeepStrictEqual } from "node:util";
|
|
63
|
+
import { AsyncStateSerializer } from "@sortedyuan/may/core";
|
|
64
|
+
import { openCodingExecutionEnvironment } from "@sortedyuan/coding-tools";
|
|
53
65
|
var maxJournalBytes = 16777216;
|
|
54
66
|
var TeamVerificationStore = class _TeamVerificationStore {
|
|
55
|
-
constructor(file, lock,
|
|
67
|
+
constructor(file, lock, checks, executionFor) {
|
|
56
68
|
this.file = file;
|
|
57
69
|
this.lock = lock;
|
|
58
|
-
this.lockPath = lockPath;
|
|
59
70
|
this.checks = checks;
|
|
71
|
+
this.executionFor = executionFor;
|
|
60
72
|
}
|
|
61
73
|
file;
|
|
62
74
|
lock;
|
|
63
|
-
lockPath;
|
|
64
75
|
checks;
|
|
76
|
+
executionFor;
|
|
65
77
|
static {
|
|
66
78
|
__name(this, "TeamVerificationStore");
|
|
67
79
|
}
|
|
@@ -76,14 +88,12 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
76
88
|
static async open(options) {
|
|
77
89
|
const checks = parseTeamChecks(options.checks ?? []);
|
|
78
90
|
await mkdir(options.directory, { recursive: true });
|
|
79
|
-
if ((await lstat(options.directory)).isSymbolicLink())
|
|
91
|
+
if ((await lstat(options.directory)).isSymbolicLink())
|
|
92
|
+
throw new Error("Verification directory must not be a symbolic link");
|
|
80
93
|
const path = join(await realpath(options.directory), "verification.jsonl");
|
|
81
|
-
const
|
|
82
|
-
const lock = await acquireFileLock(lockPath);
|
|
94
|
+
const lock = await acquireFileLock(`${path}.lock`);
|
|
83
95
|
let file;
|
|
84
96
|
try {
|
|
85
|
-
await lock.writeFile(JSON.stringify({ pid: process.pid }));
|
|
86
|
-
await lock.sync();
|
|
87
97
|
await rejectLinkIfPresent(path);
|
|
88
98
|
file = await open(path, constants.O_CREAT | constants.O_APPEND | constants.O_RDWR | noFollow(), 384);
|
|
89
99
|
const { entries, committed, size } = await readJournal(file);
|
|
@@ -96,21 +106,33 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
96
106
|
await repair.close();
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
|
-
const store = new _TeamVerificationStore(file, lock,
|
|
109
|
+
const store = new _TeamVerificationStore(file, lock, checks, options.executionFor);
|
|
100
110
|
store.size = committed;
|
|
101
111
|
for (const entry of entries) {
|
|
102
112
|
store.apply(entry.event);
|
|
103
113
|
store.sequence = entry.sequence;
|
|
104
114
|
}
|
|
105
|
-
if (entries.length && !isDeepStrictEqual(entries[0].event.checks, checks))
|
|
115
|
+
if (entries.length && !isDeepStrictEqual(entries[0].event.checks, checks))
|
|
116
|
+
throw new Error("Verification checks changed on resume");
|
|
106
117
|
if (!entries.length) await store.append({ type: "config", checks });
|
|
107
|
-
for (const result of store.results.values())
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
for (const result of store.results.values())
|
|
119
|
+
if (result.status === "pending") {
|
|
120
|
+
await store.append({
|
|
121
|
+
type: "check",
|
|
122
|
+
result: {
|
|
123
|
+
...result,
|
|
124
|
+
status: "unknown",
|
|
125
|
+
detail: "Interrupted check; inspect effects and reconcile before a new command."
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
110
129
|
return store;
|
|
111
130
|
} catch (error) {
|
|
112
|
-
|
|
113
|
-
|
|
131
|
+
try {
|
|
132
|
+
await file?.close();
|
|
133
|
+
} finally {
|
|
134
|
+
await lock.release();
|
|
135
|
+
}
|
|
114
136
|
throw error;
|
|
115
137
|
}
|
|
116
138
|
}
|
|
@@ -122,7 +144,7 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
122
144
|
try {
|
|
123
145
|
file = await open(path, constants.O_RDONLY | noFollow());
|
|
124
146
|
} catch (error) {
|
|
125
|
-
if (
|
|
147
|
+
if (hasErrorCode(error, "ENOENT")) return { reports: [], checks: [] };
|
|
126
148
|
throw error;
|
|
127
149
|
}
|
|
128
150
|
try {
|
|
@@ -152,38 +174,72 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
152
174
|
if (!spec) throw new Error("Check is not configured for this task");
|
|
153
175
|
return this.runCheck(commandId, spec, workspace, { ...options, ...extra, execution });
|
|
154
176
|
}, "runCheck");
|
|
155
|
-
const tools = /* @__PURE__ */ __name(() => [
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
const tools = /* @__PURE__ */ __name(() => [
|
|
178
|
+
{
|
|
179
|
+
name: "submit_report",
|
|
180
|
+
permissionVersion: "team-verification-v1",
|
|
181
|
+
description: "Store a structured report and file/line evidence. The host validates evidence bytes, not claim correctness. This never grants acceptance.",
|
|
182
|
+
inputSchema: {
|
|
183
|
+
type: "object",
|
|
184
|
+
additionalProperties: false,
|
|
185
|
+
required: ["summary", "claims"],
|
|
186
|
+
properties: {
|
|
187
|
+
summary: { type: "string" },
|
|
188
|
+
claims: {
|
|
189
|
+
type: "array",
|
|
190
|
+
items: {
|
|
191
|
+
type: "object",
|
|
192
|
+
additionalProperties: false,
|
|
193
|
+
required: ["text", "kind"],
|
|
194
|
+
properties: {
|
|
195
|
+
text: { type: "string" },
|
|
196
|
+
kind: { type: "string", enum: ["finding", "proposal"] },
|
|
197
|
+
evidence: {
|
|
198
|
+
type: "array",
|
|
199
|
+
items: {
|
|
200
|
+
type: "object",
|
|
201
|
+
additionalProperties: false,
|
|
202
|
+
required: ["path", "startLine", "endLine"],
|
|
203
|
+
properties: {
|
|
204
|
+
path: { type: "string" },
|
|
205
|
+
startLine: { type: "integer" },
|
|
206
|
+
endLine: { type: "integer" },
|
|
207
|
+
quote: { type: "string" }
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
parse: parseTeamReport,
|
|
217
|
+
async execute(input, call) {
|
|
218
|
+
call.signal.throwIfAborted();
|
|
219
|
+
return submit(hash(call.idempotencyKey), input);
|
|
220
|
+
}
|
|
181
221
|
},
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
222
|
+
{
|
|
223
|
+
name: "run_check",
|
|
224
|
+
permissionVersion: "team-verification-v1",
|
|
225
|
+
description: `Run one exact host-configured check by id; no model-supplied commands or arguments. Available: ${this.checks.filter((entry) => entry.taskId === identity.taskId).map((entry) => entry.id).join(", ") || "none"}. Command checks require explicit host authorization and are not sandboxed.`,
|
|
226
|
+
inputSchema: {
|
|
227
|
+
type: "object",
|
|
228
|
+
additionalProperties: false,
|
|
229
|
+
required: ["id"],
|
|
230
|
+
properties: { id: { type: "string" } }
|
|
231
|
+
},
|
|
232
|
+
parse(value) {
|
|
233
|
+
const input = fields(value, ["id"]);
|
|
234
|
+
identifier(input.id, "check id");
|
|
235
|
+
return { id: input.id };
|
|
236
|
+
},
|
|
237
|
+
async execute(input, call) {
|
|
238
|
+
call.signal.throwIfAborted();
|
|
239
|
+
return runCheck(hash(call.idempotencyKey), input.id, { signal: call.signal });
|
|
240
|
+
}
|
|
185
241
|
}
|
|
186
|
-
|
|
242
|
+
], "tools");
|
|
187
243
|
return { submit, runCheck, tools };
|
|
188
244
|
}
|
|
189
245
|
runCheck(commandId, input, workspace, options = {}) {
|
|
@@ -195,16 +251,19 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
195
251
|
/** Host reconciliation never invents a passing result and never executes a command. */
|
|
196
252
|
async reconcile(commandId, outcome, evidence) {
|
|
197
253
|
boundedString(evidence, "reconciliation evidence", 8192);
|
|
198
|
-
if (outcome.status !== "failed" && outcome.status !== "cancelled")
|
|
254
|
+
if (outcome.status !== "failed" && outcome.status !== "cancelled")
|
|
255
|
+
throw new Error("Reconciliation cannot grant acceptance");
|
|
199
256
|
return this.serial.run(async () => {
|
|
200
257
|
this.assertOpen();
|
|
201
258
|
const current = this.results.get(commandId);
|
|
202
259
|
if (!current) throw new Error("Unknown check command");
|
|
203
260
|
if (current.reconciliation) {
|
|
204
|
-
if (current.status !== outcome.status || current.reconciliation !== evidence)
|
|
261
|
+
if (current.status !== outcome.status || current.reconciliation !== evidence)
|
|
262
|
+
throw new Error("Reconciliation already recorded differently");
|
|
205
263
|
return structuredClone(current);
|
|
206
264
|
}
|
|
207
|
-
if (current.status !== "unknown")
|
|
265
|
+
if (current.status !== "unknown")
|
|
266
|
+
throw new Error("Only an unknown check can be reconciled; an active pending check must first stop");
|
|
208
267
|
const result = { ...current, status: outcome.status, reconciliation: evidence };
|
|
209
268
|
await this.write({ type: "check", result });
|
|
210
269
|
return structuredClone(result);
|
|
@@ -216,10 +275,17 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
216
275
|
const identity = execution ? taskIdentity(execution) : void 0;
|
|
217
276
|
const report = state.reports.filter((entry) => entry.taskId === taskId && (!identity || sameIdentity(entry, identity))).at(-1);
|
|
218
277
|
const checks = this.checks.filter((spec) => spec.taskId === taskId).map((spec) => {
|
|
219
|
-
const result = state.checks.filter(
|
|
278
|
+
const result = state.checks.filter(
|
|
279
|
+
(entry) => entry.spec.id === spec.id && (!identity || entry.dispatchId === void 0 || sameIdentity(entry, identity))
|
|
280
|
+
).at(-1);
|
|
220
281
|
if (!result) return { id: spec.id, status: "missing" };
|
|
221
282
|
const stale = result.workspaceFingerprint !== fingerprint || result.afterFingerprint !== fingerprint;
|
|
222
|
-
return {
|
|
283
|
+
return {
|
|
284
|
+
id: spec.id,
|
|
285
|
+
status: stale && !["pending", "unknown"].includes(result.status) ? "stale" : result.status,
|
|
286
|
+
commandId: result.commandId,
|
|
287
|
+
...result.detail ? { detail: result.detail } : {}
|
|
288
|
+
};
|
|
223
289
|
});
|
|
224
290
|
const currentReport = report?.workspaceFingerprint === fingerprint;
|
|
225
291
|
const badEvidence = currentReport && report.evidence.some((item) => !item.valid);
|
|
@@ -239,7 +305,7 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
239
305
|
try {
|
|
240
306
|
await this.file.close();
|
|
241
307
|
} finally {
|
|
242
|
-
await
|
|
308
|
+
await this.lock.release();
|
|
243
309
|
}
|
|
244
310
|
})();
|
|
245
311
|
}
|
|
@@ -250,32 +316,41 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
250
316
|
this.assertOpen();
|
|
251
317
|
const old = this.reports.find((entry) => entry.commandId === commandId);
|
|
252
318
|
if (old) {
|
|
253
|
-
if (!sameIdentity(old, identity) || !isDeepStrictEqual(old.report, report))
|
|
319
|
+
if (!sameIdentity(old, identity) || !isDeepStrictEqual(old.report, report))
|
|
320
|
+
throw new Error("Report command already used with different content or identity");
|
|
254
321
|
return structuredClone(old);
|
|
255
322
|
}
|
|
256
323
|
if (this.reports.length >= 256) throw new Error("Report quota exceeded");
|
|
257
324
|
const fingerprint = await workspaceFingerprint(workspace);
|
|
258
325
|
const evidence = [];
|
|
259
|
-
for (const claim of report.claims)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
326
|
+
for (const claim of report.claims)
|
|
327
|
+
for (const item of claim.evidence ?? []) {
|
|
328
|
+
try {
|
|
329
|
+
const bytes = await readWorkspaceFile(workspace, item.path);
|
|
330
|
+
const lines2 = bytes.toString("utf8").replace(/\r\n/gu, "\n").split("\n");
|
|
331
|
+
if (lines2.at(-1) === "") lines2.pop();
|
|
332
|
+
const excerpt = lines2.slice(item.startLine - 1, item.endLine).join("\n");
|
|
333
|
+
const valid = item.endLine <= lines2.length && (item.quote === void 0 || excerpt.includes(item.quote.replace(/\r\n/gu, "\n")));
|
|
334
|
+
evidence.push({
|
|
335
|
+
path: item.path,
|
|
336
|
+
startLine: item.startLine,
|
|
337
|
+
endLine: item.endLine,
|
|
338
|
+
sha256: hash(bytes),
|
|
339
|
+
valid,
|
|
340
|
+
...!valid ? { detail: "Line range or quote does not match the file" } : {}
|
|
341
|
+
});
|
|
342
|
+
} catch (error) {
|
|
343
|
+
evidence.push({
|
|
344
|
+
path: item.path,
|
|
345
|
+
startLine: item.startLine,
|
|
346
|
+
endLine: item.endLine,
|
|
347
|
+
valid: false,
|
|
348
|
+
detail: message(error)
|
|
349
|
+
});
|
|
350
|
+
}
|
|
276
351
|
}
|
|
277
|
-
|
|
278
|
-
|
|
352
|
+
if (await workspaceFingerprint(workspace) !== fingerprint)
|
|
353
|
+
throw new Error("Workspace changed while validating report evidence");
|
|
279
354
|
const stored = { ...identity, commandId, report, workspaceFingerprint: fingerprint, evidence };
|
|
280
355
|
await this.write({ type: "report", report: stored });
|
|
281
356
|
return structuredClone(stored);
|
|
@@ -293,14 +368,25 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
293
368
|
this.assertOpen();
|
|
294
369
|
const old = this.results.get(commandId);
|
|
295
370
|
if (old) {
|
|
296
|
-
if (!isDeepStrictEqual(old.spec, spec) || !sameIdentity(old, identity))
|
|
371
|
+
if (!isDeepStrictEqual(old.spec, spec) || !sameIdentity(old, identity))
|
|
372
|
+
throw new Error("Check command already used with different specification or identity");
|
|
297
373
|
return structuredClone(old);
|
|
298
374
|
}
|
|
299
375
|
if (this.results.size >= 512) throw new Error("Check quota exceeded");
|
|
300
|
-
if ([...this.results.values()].some(
|
|
301
|
-
|
|
376
|
+
if ([...this.results.values()].some(
|
|
377
|
+
(entry) => entry.taskId === spec.taskId && ["unknown", "pending"].includes(entry.status)
|
|
378
|
+
))
|
|
379
|
+
throw new Error("Task has a pending or unknown check; inspect and reconcile before another check");
|
|
380
|
+
if (spec.type === "command" && options.allowCommands !== true)
|
|
381
|
+
throw new Error("Command checks require explicit --allow-checks authorization");
|
|
302
382
|
options.signal?.throwIfAborted();
|
|
303
|
-
const result2 = {
|
|
383
|
+
const result2 = {
|
|
384
|
+
...identity,
|
|
385
|
+
commandId,
|
|
386
|
+
spec,
|
|
387
|
+
workspaceFingerprint: await workspaceFingerprint(workspace),
|
|
388
|
+
status: "pending"
|
|
389
|
+
};
|
|
304
390
|
await this.write({ type: "check", result: result2 });
|
|
305
391
|
fresh = true;
|
|
306
392
|
return result2;
|
|
@@ -308,12 +394,15 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
308
394
|
if (!fresh) return pending;
|
|
309
395
|
let outcome;
|
|
310
396
|
try {
|
|
311
|
-
if (spec.type === "command") outcome = await runCommand(spec, workspace, options.signal);
|
|
397
|
+
if (spec.type === "command") outcome = await runCommand(spec, workspace, options.signal, this.executionFor);
|
|
312
398
|
else {
|
|
313
399
|
options.signal?.throwIfAborted();
|
|
314
400
|
const bytes = await readWorkspaceFile(workspace, spec.path);
|
|
315
401
|
const passed = spec.type === "file-sha256" ? hash(bytes) === spec.sha256 : bytes.toString("utf8").includes(spec.text);
|
|
316
|
-
outcome = {
|
|
402
|
+
outcome = {
|
|
403
|
+
status: passed ? "passed" : "failed",
|
|
404
|
+
detail: passed ? "Exact configured file check passed" : "Configured file predicate did not match"
|
|
405
|
+
};
|
|
317
406
|
}
|
|
318
407
|
} catch (error) {
|
|
319
408
|
outcome = { status: spec.type === "command" ? "unknown" : "failed", detail: message(error) };
|
|
@@ -322,7 +411,11 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
322
411
|
try {
|
|
323
412
|
afterFingerprint = await workspaceFingerprint(workspace);
|
|
324
413
|
} catch (error) {
|
|
325
|
-
outcome = {
|
|
414
|
+
outcome = {
|
|
415
|
+
...outcome,
|
|
416
|
+
status: spec.type === "command" ? "unknown" : "failed",
|
|
417
|
+
detail: `Cannot inspect workspace after check: ${message(error)}`
|
|
418
|
+
};
|
|
326
419
|
}
|
|
327
420
|
const result = { ...pending, ...outcome, ...afterFingerprint ? { afterFingerprint } : {} };
|
|
328
421
|
await this.append({ type: "check", result });
|
|
@@ -357,7 +450,18 @@ var TeamVerificationStore = class _TeamVerificationStore {
|
|
|
357
450
|
}
|
|
358
451
|
};
|
|
359
452
|
function parseTeamCheck(value) {
|
|
360
|
-
const raw = fields(value, [
|
|
453
|
+
const raw = fields(value, [
|
|
454
|
+
"id",
|
|
455
|
+
"taskId",
|
|
456
|
+
"type",
|
|
457
|
+
"path",
|
|
458
|
+
"text",
|
|
459
|
+
"sha256",
|
|
460
|
+
"command",
|
|
461
|
+
"args",
|
|
462
|
+
"timeoutMs",
|
|
463
|
+
"maxOutputBytes"
|
|
464
|
+
]);
|
|
361
465
|
identifier(raw.id, "check id");
|
|
362
466
|
identifier(raw.taskId, "check taskId");
|
|
363
467
|
if (raw.type === "file-contains") {
|
|
@@ -371,7 +475,8 @@ function parseTeamCheck(value) {
|
|
|
371
475
|
} else if (raw.type === "command") {
|
|
372
476
|
fields(raw, ["id", "taskId", "type", "command", "args", "timeoutMs", "maxOutputBytes"]);
|
|
373
477
|
boundedString(raw.command, "check command", 4096);
|
|
374
|
-
if (/[\u0000\r\n]/u.test(raw.command) || !Array.isArray(raw.args) || raw.args.length > 64 || raw.args.some((arg) => typeof arg !== "string" || arg.length > 8192 || arg.includes("\0")))
|
|
478
|
+
if (/[\u0000\r\n]/u.test(raw.command) || !Array.isArray(raw.args) || raw.args.length > 64 || raw.args.some((arg) => typeof arg !== "string" || arg.length > 8192 || arg.includes("\0")))
|
|
479
|
+
throw new Error("Invalid check command arguments");
|
|
375
480
|
if (raw.timeoutMs !== void 0) integer(raw.timeoutMs, 1, 12e4, "check timeoutMs");
|
|
376
481
|
if (raw.maxOutputBytes !== void 0) integer(raw.maxOutputBytes, 1, 1048576, "check maxOutputBytes");
|
|
377
482
|
} else throw new Error("Unknown check type");
|
|
@@ -388,7 +493,8 @@ __name(parseTeamChecks, "parseTeamChecks");
|
|
|
388
493
|
function parseTeamReport(value) {
|
|
389
494
|
const report = fields(value, ["summary", "claims"]);
|
|
390
495
|
boundedString(report.summary, "report summary", 8192);
|
|
391
|
-
if (!Array.isArray(report.claims) || report.claims.length > 64)
|
|
496
|
+
if (!Array.isArray(report.claims) || report.claims.length > 64)
|
|
497
|
+
throw new Error("Report claims must be an array of at most 64 entries");
|
|
392
498
|
for (const value2 of report.claims) {
|
|
393
499
|
const claim = fields(value2, ["text", "kind", "evidence"]);
|
|
394
500
|
boundedString(claim.text, "claim text", 4096);
|
|
@@ -400,7 +506,8 @@ function parseTeamReport(value) {
|
|
|
400
506
|
safeRelative(evidence.path);
|
|
401
507
|
integer(evidence.startLine, 1, 1e6, "evidence startLine");
|
|
402
508
|
integer(evidence.endLine, evidence.startLine, 1e6, "evidence endLine");
|
|
403
|
-
if (evidence.endLine - evidence.startLine > 1e3)
|
|
509
|
+
if (evidence.endLine - evidence.startLine > 1e3)
|
|
510
|
+
throw new Error("Evidence range exceeds 1001 lines");
|
|
404
511
|
if (evidence.quote !== void 0) boundedString(evidence.quote, "evidence quote", 8192);
|
|
405
512
|
}
|
|
406
513
|
}
|
|
@@ -421,7 +528,8 @@ async function workspaceFingerprint(directory) {
|
|
|
421
528
|
const child = join(path, name);
|
|
422
529
|
const stat2 = await lstat(child);
|
|
423
530
|
const nameInRoot = relative(root, child).split(sep).join("/");
|
|
424
|
-
if (++files > 1e4 || stat2.isSymbolicLink())
|
|
531
|
+
if (++files > 1e4 || stat2.isSymbolicLink())
|
|
532
|
+
throw new Error("Workspace exceeds 10000 entries or contains a symbolic link");
|
|
425
533
|
if (stat2.isDirectory()) {
|
|
426
534
|
entries.push([nameInRoot, "directory"]);
|
|
427
535
|
await visit(child);
|
|
@@ -447,13 +555,15 @@ async function readWorkspaceFile(directory, path) {
|
|
|
447
555
|
}
|
|
448
556
|
const canonical = await realpath(target);
|
|
449
557
|
const rel = relative(root, canonical);
|
|
450
|
-
if (!rel || rel.startsWith(`..${sep}`) || rel === ".." || isAbsolute(rel))
|
|
558
|
+
if (!rel || rel.startsWith(`..${sep}`) || rel === ".." || isAbsolute(rel))
|
|
559
|
+
throw new Error("Evidence/check path escapes workspace");
|
|
451
560
|
const before = await lstat(target);
|
|
452
561
|
if (!before.isFile() || before.size > 67108864) throw new Error("Evidence/check file is invalid or exceeds 64 MiB");
|
|
453
562
|
const file = await open(target, constants.O_RDONLY | noFollow());
|
|
454
563
|
try {
|
|
455
564
|
const current = await file.stat();
|
|
456
|
-
if (!current.isFile() || current.dev !== before.dev || current.ino !== before.ino || current.size !== before.size)
|
|
565
|
+
if (!current.isFile() || current.dev !== before.dev || current.ino !== before.ino || current.size !== before.size)
|
|
566
|
+
throw new Error("File changed while opening");
|
|
457
567
|
const buffer = Buffer.alloc(before.size + 1);
|
|
458
568
|
let length = 0;
|
|
459
569
|
while (length < buffer.length) {
|
|
@@ -462,43 +572,79 @@ async function readWorkspaceFile(directory, path) {
|
|
|
462
572
|
length += chunk.bytesRead;
|
|
463
573
|
}
|
|
464
574
|
const after = await file.stat();
|
|
465
|
-
if (length !== before.size || after.size !== current.size || after.mtimeMs !== current.mtimeMs)
|
|
575
|
+
if (length !== before.size || after.size !== current.size || after.mtimeMs !== current.mtimeMs)
|
|
576
|
+
throw new Error("File changed while reading");
|
|
466
577
|
return buffer.subarray(0, length);
|
|
467
578
|
} finally {
|
|
468
579
|
await file.close();
|
|
469
580
|
}
|
|
470
581
|
}
|
|
471
582
|
__name(readWorkspaceFile, "readWorkspaceFile");
|
|
472
|
-
async function runCommand(spec, workspace, signal) {
|
|
583
|
+
async function runCommand(spec, workspace, signal, executionFor) {
|
|
584
|
+
const environment = executionFor ? await executionFor(workspace) : await openCodingExecutionEnvironment({ workspace, isolation: { mode: "host" } });
|
|
585
|
+
try {
|
|
586
|
+
return await runEnvironmentCommand(spec, workspace, environment, signal);
|
|
587
|
+
} finally {
|
|
588
|
+
if (!executionFor) await environment.close();
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
__name(runCommand, "runCommand");
|
|
592
|
+
async function runEnvironmentCommand(spec, workspace, environment, signal) {
|
|
473
593
|
signal?.throwIfAborted();
|
|
474
|
-
if (process.platform === "win32" && /\.(?:cmd|bat)$/iu.test(spec.command))
|
|
594
|
+
if (process.platform === "win32" && /\.(?:cmd|bat)$/iu.test(spec.command))
|
|
595
|
+
return {
|
|
596
|
+
status: "failed",
|
|
597
|
+
detail: "Command checks execute native programs directly. Use node.exe with the package manager JavaScript entry point, not .cmd/.bat."
|
|
598
|
+
};
|
|
475
599
|
const cwd = await realpath(workspace);
|
|
476
600
|
const env = {};
|
|
477
|
-
for (const key of [
|
|
601
|
+
for (const key of [
|
|
602
|
+
"PATH",
|
|
603
|
+
"Path",
|
|
604
|
+
"PATHEXT",
|
|
605
|
+
"SystemRoot",
|
|
606
|
+
"WINDIR",
|
|
607
|
+
"COMSPEC",
|
|
608
|
+
"TEMP",
|
|
609
|
+
"TMP",
|
|
610
|
+
"TMPDIR",
|
|
611
|
+
"LANG",
|
|
612
|
+
"LC_ALL"
|
|
613
|
+
])
|
|
614
|
+
if (environment.isolation.mode === "host" && process.env[key] !== void 0) env[key] = process.env[key];
|
|
478
615
|
env.CI = "1";
|
|
479
|
-
return new Promise((resolveResult) => {
|
|
616
|
+
return new Promise((resolveResult, rejectResult) => {
|
|
617
|
+
const cancellation = new AbortController();
|
|
480
618
|
let output = Buffer.alloc(0);
|
|
481
619
|
let reason;
|
|
482
620
|
let settled = false;
|
|
483
|
-
const child = spawn(spec.command,
|
|
621
|
+
const child = environment.spawn(spec.command, spec.args, {
|
|
622
|
+
cwd,
|
|
623
|
+
env,
|
|
624
|
+
signal: cancellation.signal
|
|
625
|
+
});
|
|
626
|
+
const stdout = child.stdout;
|
|
627
|
+
const stderr = child.stderr;
|
|
628
|
+
if (!stdout || !stderr) {
|
|
629
|
+
rejectResult(new Error("Execution environment must provide command output pipes"));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
484
632
|
const finish = /* @__PURE__ */ __name((status, exitCode, detail) => {
|
|
485
633
|
if (settled) return;
|
|
486
634
|
settled = true;
|
|
487
635
|
clearTimeout(timer);
|
|
488
|
-
clearTimeout(killWait);
|
|
489
636
|
signal?.removeEventListener("abort", abort);
|
|
490
|
-
resolveResult({
|
|
637
|
+
resolveResult({
|
|
638
|
+
status,
|
|
639
|
+
...exitCode !== void 0 ? { exitCode } : {},
|
|
640
|
+
...detail ? { detail } : {},
|
|
641
|
+
output: boundedOutput(output, spec.maxOutputBytes ?? 65536)
|
|
642
|
+
});
|
|
491
643
|
}, "finish");
|
|
492
|
-
let killWait;
|
|
493
644
|
const stop = /* @__PURE__ */ __name((detail) => {
|
|
494
645
|
if (reason) return;
|
|
495
646
|
reason = detail;
|
|
496
|
-
|
|
497
|
-
killWait = setTimeout(() => {
|
|
498
|
-
child.stdout.destroy();
|
|
499
|
-
child.stderr.destroy();
|
|
500
|
-
finish("unknown", void 0, `${detail}; child effects require inspection`);
|
|
501
|
-
}, 1e3);
|
|
647
|
+
cancellation.abort();
|
|
502
648
|
}, "stop");
|
|
503
649
|
const abort = /* @__PURE__ */ __name(() => stop("Check interrupted"), "abort");
|
|
504
650
|
const timer = setTimeout(() => stop("Check timed out"), spec.timeoutMs ?? 3e4);
|
|
@@ -509,18 +655,24 @@ async function runCommand(spec, workspace, signal) {
|
|
|
509
655
|
stop("Check output limit exceeded");
|
|
510
656
|
} else output = Buffer.concat([output, chunk]);
|
|
511
657
|
}, "collect");
|
|
512
|
-
|
|
513
|
-
|
|
658
|
+
stdout.on("data", collect);
|
|
659
|
+
stderr.on("data", collect);
|
|
514
660
|
child.on("error", (error) => finish(child.pid === void 0 ? "failed" : "unknown", void 0, message(error)));
|
|
515
661
|
child.on("close", (code, terminationSignal) => {
|
|
516
|
-
if (reason || terminationSignal || code === null)
|
|
517
|
-
|
|
662
|
+
if (reason || terminationSignal || code === null)
|
|
663
|
+
finish("unknown", void 0, reason ?? `Check ended by ${terminationSignal ?? "unknown termination"}`);
|
|
664
|
+
else
|
|
665
|
+
finish(
|
|
666
|
+
code === 0 ? "passed" : "failed",
|
|
667
|
+
code,
|
|
668
|
+
code === 0 ? "Configured command exited successfully" : "Configured command exited unsuccessfully"
|
|
669
|
+
);
|
|
518
670
|
});
|
|
519
671
|
signal?.addEventListener("abort", abort, { once: true });
|
|
520
672
|
if (signal?.aborted) abort();
|
|
521
673
|
});
|
|
522
674
|
}
|
|
523
|
-
__name(
|
|
675
|
+
__name(runEnvironmentCommand, "runEnvironmentCommand");
|
|
524
676
|
async function readJournal(file) {
|
|
525
677
|
const stat2 = await file.stat();
|
|
526
678
|
if (!stat2.isFile() || stat2.size > maxJournalBytes) throw new Error("Invalid verification journal type or size");
|
|
@@ -538,7 +690,8 @@ async function readJournal(file) {
|
|
|
538
690
|
let specs = [];
|
|
539
691
|
for (const line of bytes.subarray(0, committed).toString("utf8").split("\n").slice(0, -1)) {
|
|
540
692
|
const entry = JSON.parse(line);
|
|
541
|
-
if (!entry || entry.sequence !== entries.length + 1 || !entry.event)
|
|
693
|
+
if (!entry || entry.sequence !== entries.length + 1 || !entry.event)
|
|
694
|
+
throw new Error("Invalid verification journal sequence");
|
|
542
695
|
const event = entry.event;
|
|
543
696
|
if (event.type === "config") {
|
|
544
697
|
if (entries.length !== 0) throw new Error("Duplicate verification config");
|
|
@@ -547,24 +700,32 @@ async function readJournal(file) {
|
|
|
547
700
|
else if (event.type === "report") {
|
|
548
701
|
parseTeamReport(event.report.report);
|
|
549
702
|
validateStored(event.report);
|
|
550
|
-
if (reportIds.size >= 256 || reportIds.has(event.report.commandId) || !Array.isArray(event.report.evidence))
|
|
703
|
+
if (reportIds.size >= 256 || reportIds.has(event.report.commandId) || !Array.isArray(event.report.evidence))
|
|
704
|
+
throw new Error("Invalid stored report history");
|
|
551
705
|
for (const item of event.report.evidence) {
|
|
552
706
|
safeRelative(item.path);
|
|
553
707
|
integer(item.startLine, 1, 1e6, "stored evidence startLine");
|
|
554
708
|
integer(item.endLine, item.startLine, 1e6, "stored evidence endLine");
|
|
555
|
-
if (typeof item.valid !== "boolean" || item.valid && !/^[a-f0-9]{64}$/u.test(item.sha256 ?? ""))
|
|
709
|
+
if (typeof item.valid !== "boolean" || item.valid && !/^[a-f0-9]{64}$/u.test(item.sha256 ?? ""))
|
|
710
|
+
throw new Error("Invalid stored evidence");
|
|
556
711
|
}
|
|
557
712
|
reportIds.add(event.report.commandId);
|
|
558
713
|
} else if (event.type === "check") {
|
|
559
714
|
const result = event.result;
|
|
560
715
|
parseTeamCheck(result.spec);
|
|
561
716
|
validateStored(result);
|
|
562
|
-
if (!isDeepStrictEqual(
|
|
717
|
+
if (!isDeepStrictEqual(
|
|
718
|
+
specs.find((entry2) => entry2.id === result.spec.id),
|
|
719
|
+
result.spec
|
|
720
|
+
) || result.taskId !== result.spec.taskId || !["pending", "passed", "failed", "unknown", "cancelled"].includes(result.status) || result.afterFingerprint !== void 0 && !/^[a-f0-9]{64}$/u.test(result.afterFingerprint))
|
|
721
|
+
throw new Error("Invalid check record");
|
|
563
722
|
const old = checkResults.get(result.commandId);
|
|
564
723
|
if (!old) {
|
|
565
|
-
if (checkResults.size >= 512 || result.status !== "pending")
|
|
724
|
+
if (checkResults.size >= 512 || result.status !== "pending")
|
|
725
|
+
throw new Error("Check result has no pending intent");
|
|
566
726
|
} else {
|
|
567
|
-
if (!sameIdentity(old, result) || !isDeepStrictEqual(old.spec, result.spec) || old.workspaceFingerprint !== result.workspaceFingerprint || old.status !== "pending" && old.status !== "unknown" || result.status === "pending" || old.status === "unknown" && (!["failed", "cancelled"].includes(result.status) || !result.reconciliation))
|
|
727
|
+
if (!sameIdentity(old, result) || !isDeepStrictEqual(old.spec, result.spec) || old.workspaceFingerprint !== result.workspaceFingerprint || old.status !== "pending" && old.status !== "unknown" || result.status === "pending" || old.status === "unknown" && (!["failed", "cancelled"].includes(result.status) || !result.reconciliation))
|
|
728
|
+
throw new Error("Invalid check history transition");
|
|
568
729
|
}
|
|
569
730
|
checkResults.set(result.commandId, result);
|
|
570
731
|
} else throw new Error("Unknown verification journal record");
|
|
@@ -580,7 +741,12 @@ function validateStored(value) {
|
|
|
580
741
|
}
|
|
581
742
|
__name(validateStored, "validateStored");
|
|
582
743
|
function taskIdentity(execution) {
|
|
583
|
-
return {
|
|
744
|
+
return {
|
|
745
|
+
taskId: execution.task.id,
|
|
746
|
+
coordinationId: execution.coordinationId,
|
|
747
|
+
dispatchId: execution.task.dispatchId,
|
|
748
|
+
turn: execution.task.turn ?? 0
|
|
749
|
+
};
|
|
584
750
|
}
|
|
585
751
|
__name(taskIdentity, "taskIdentity");
|
|
586
752
|
function sameIdentity(a, b) {
|
|
@@ -588,39 +754,40 @@ function sameIdentity(a, b) {
|
|
|
588
754
|
}
|
|
589
755
|
__name(sameIdentity, "sameIdentity");
|
|
590
756
|
function fields(value, allowed) {
|
|
591
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !allowed.includes(key)))
|
|
757
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !allowed.includes(key)))
|
|
758
|
+
throw new Error("Invalid verification fields");
|
|
592
759
|
return value;
|
|
593
760
|
}
|
|
594
761
|
__name(fields, "fields");
|
|
595
762
|
function identifier(value, label) {
|
|
596
|
-
if (typeof value !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,255}$/u.test(value))
|
|
763
|
+
if (typeof value !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,255}$/u.test(value))
|
|
764
|
+
throw new Error(`Invalid ${label}`);
|
|
597
765
|
}
|
|
598
766
|
__name(identifier, "identifier");
|
|
599
767
|
function boundedString(value, label, max) {
|
|
600
|
-
if (typeof value !== "string" || value.length < 1 || Buffer.byteLength(value) > max || value.includes("\0"))
|
|
768
|
+
if (typeof value !== "string" || value.length < 1 || Buffer.byteLength(value) > max || value.includes("\0"))
|
|
769
|
+
throw new Error(`Invalid ${label}`);
|
|
601
770
|
}
|
|
602
771
|
__name(boundedString, "boundedString");
|
|
603
772
|
function integer(value, min, max, label) {
|
|
604
|
-
if (!Number.isSafeInteger(value) || value < min || value > max)
|
|
773
|
+
if (!Number.isSafeInteger(value) || value < min || value > max)
|
|
774
|
+
throw new Error(`Invalid ${label}`);
|
|
605
775
|
}
|
|
606
776
|
__name(integer, "integer");
|
|
607
777
|
function safeRelative(value) {
|
|
608
778
|
boundedString(value, "workspace relative path", 2048);
|
|
609
|
-
if (isAbsolute(value) || /[:\u0000-\u001f]/u.test(value) || value.replace(/\\/gu, "/").split("/").some((part) => !part || part === "." || part === ".."))
|
|
779
|
+
if (isAbsolute(value) || /[:\u0000-\u001f]/u.test(value) || value.replace(/\\/gu, "/").split("/").some((part) => !part || part === "." || part === ".."))
|
|
780
|
+
throw new Error("Unsafe workspace relative path");
|
|
610
781
|
}
|
|
611
782
|
__name(safeRelative, "safeRelative");
|
|
612
783
|
async function rejectLinkIfPresent(path) {
|
|
613
784
|
try {
|
|
614
785
|
if ((await lstat(path)).isSymbolicLink()) throw new Error("Verification file must not be a symbolic link");
|
|
615
786
|
} catch (error) {
|
|
616
|
-
if (!
|
|
787
|
+
if (!hasErrorCode(error, "ENOENT")) throw error;
|
|
617
788
|
}
|
|
618
789
|
}
|
|
619
790
|
__name(rejectLinkIfPresent, "rejectLinkIfPresent");
|
|
620
|
-
function isMissing(error) {
|
|
621
|
-
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
622
|
-
}
|
|
623
|
-
__name(isMissing, "isMissing");
|
|
624
791
|
function noFollow() {
|
|
625
792
|
return process.platform === "win32" ? 0 : constants.O_NOFOLLOW;
|
|
626
793
|
}
|
|
@@ -649,8 +816,17 @@ __name(boundedOutput, "boundedOutput");
|
|
|
649
816
|
|
|
650
817
|
// apps/maybecode/src/team-plan.ts
|
|
651
818
|
var MAX_PLAN_BYTES = 1048576;
|
|
652
|
-
var READ_TOOLS = [
|
|
653
|
-
|
|
819
|
+
var READ_TOOLS = [
|
|
820
|
+
"read",
|
|
821
|
+
"glob",
|
|
822
|
+
"grep",
|
|
823
|
+
"list_files",
|
|
824
|
+
"publish_artifact",
|
|
825
|
+
"read_artifact",
|
|
826
|
+
"submit_report",
|
|
827
|
+
"run_check"
|
|
828
|
+
];
|
|
829
|
+
var ALL_TOOLS = [...READ_TOOLS, "fetch", "write", "edit"];
|
|
654
830
|
var LIMIT_CAPS = {
|
|
655
831
|
maxConcurrent: 8,
|
|
656
832
|
maxTasks: 128,
|
|
@@ -672,36 +848,48 @@ function parseTeamPlan(value, options = {}) {
|
|
|
672
848
|
if (plan.format !== 1) fail("plan.format must be 1");
|
|
673
849
|
const name = plan.name === void 0 ? void 0 : text(plan.name, "plan.name", 128);
|
|
674
850
|
const roleValues = object(plan.roles, "plan.roles");
|
|
675
|
-
if (Object.keys(roleValues).length < 1 || Object.keys(roleValues).length > 16)
|
|
851
|
+
if (Object.keys(roleValues).length < 1 || Object.keys(roleValues).length > 16)
|
|
852
|
+
fail("plan.roles must contain 1-16 roles");
|
|
676
853
|
const roles = {};
|
|
677
854
|
for (const [id, value2] of Object.entries(roleValues)) {
|
|
678
855
|
identifier2(id, "role id");
|
|
679
|
-
const role = object(value2, `role ${id}`, [
|
|
856
|
+
const role = object(value2, `role ${id}`, [
|
|
857
|
+
"model",
|
|
858
|
+
"instructions",
|
|
859
|
+
"tools",
|
|
860
|
+
"delegateTo",
|
|
861
|
+
"messaging",
|
|
862
|
+
"runBudget"
|
|
863
|
+
]);
|
|
680
864
|
const model = role.model === void 0 ? void 0 : text(role.model, `role ${id}.model`, 128);
|
|
681
865
|
const instructions2 = role.instructions === void 0 ? void 0 : text(role.instructions, `role ${id}.instructions`, 16384);
|
|
682
866
|
const tools = role.tools === void 0 ? [...READ_TOOLS] : stringArray(role.tools, `role ${id}.tools`, ALL_TOOLS.length);
|
|
683
867
|
for (const tool of tools) {
|
|
684
868
|
if (!ALL_TOOLS.includes(tool)) fail(`Unknown tool in role ${id}: ${tool}`);
|
|
685
|
-
if (mode === "read-only" && (tool === "write" || tool === "edit"))
|
|
869
|
+
if (mode === "read-only" && (tool === "write" || tool === "edit"))
|
|
870
|
+
fail(`Tool ${tool} in role ${id} requires --mode coding`);
|
|
686
871
|
}
|
|
687
872
|
const delegateTo = role.delegateTo === void 0 ? [] : stringArray(role.delegateTo, `role ${id}.delegateTo`, 16);
|
|
688
873
|
delegateTo.forEach((target) => identifier2(target, `role ${id}.delegateTo`));
|
|
689
|
-
if (role.messaging !== void 0 && typeof role.messaging !== "boolean")
|
|
874
|
+
if (role.messaging !== void 0 && typeof role.messaging !== "boolean")
|
|
875
|
+
fail(`role ${id}.messaging must be boolean`);
|
|
690
876
|
const runBudget = role.runBudget === void 0 ? void 0 : budget(role.runBudget, `role ${id}.runBudget`);
|
|
691
|
-
roles[id] = {
|
|
692
|
-
|
|
693
|
-
|
|
877
|
+
roles[id] = omitUndefined({
|
|
878
|
+
model,
|
|
879
|
+
instructions: instructions2,
|
|
694
880
|
tools,
|
|
695
881
|
delegateTo,
|
|
696
882
|
messaging: role.messaging ?? false,
|
|
697
|
-
|
|
698
|
-
};
|
|
883
|
+
runBudget
|
|
884
|
+
});
|
|
699
885
|
}
|
|
700
886
|
for (const [id, role] of Object.entries(roles)) {
|
|
701
|
-
for (const target of role.delegateTo)
|
|
887
|
+
for (const target of role.delegateTo)
|
|
888
|
+
if (!Object.hasOwn(roles, target)) fail(`Unknown delegated role ${target} in role ${id}`);
|
|
702
889
|
}
|
|
703
890
|
const limits = plan.limits === void 0 ? void 0 : parseLimits(plan.limits);
|
|
704
|
-
if (!Array.isArray(plan.tasks) || plan.tasks.length < 1 || plan.tasks.length > (limits?.maxTasks ?? 128))
|
|
891
|
+
if (!Array.isArray(plan.tasks) || plan.tasks.length < 1 || plan.tasks.length > (limits?.maxTasks ?? 128))
|
|
892
|
+
fail("plan.tasks exceeds its task quota or is empty");
|
|
705
893
|
const tasks = plan.tasks.map((value2, index) => {
|
|
706
894
|
const task = object(value2, `task ${index}`, ["id", "agent", "input", "dependsOn"]);
|
|
707
895
|
const id = identifier2(task.id, "task id");
|
|
@@ -721,16 +909,10 @@ function parseTeamPlan(value, options = {}) {
|
|
|
721
909
|
} catch (error) {
|
|
722
910
|
fail(`plan.checks: ${error instanceof Error ? error.message : "Invalid checks"}`);
|
|
723
911
|
}
|
|
724
|
-
for (const check of checks)
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
roles,
|
|
729
|
-
tasks,
|
|
730
|
-
resultTaskId,
|
|
731
|
-
...limits === void 0 ? {} : { limits },
|
|
732
|
-
checks
|
|
733
|
-
};
|
|
912
|
+
for (const check of checks)
|
|
913
|
+
if (!tasks.some((task) => task.id === check.taskId))
|
|
914
|
+
fail(`Check ${check.id} references unknown task ${check.taskId}`);
|
|
915
|
+
const result = omitUndefined({ format: 1, name, roles, tasks, resultTaskId, limits, checks });
|
|
734
916
|
if (Buffer.byteLength(JSON.stringify(result), "utf8") > MAX_PLAN_BYTES) fail("Team plan exceeds 1 MiB");
|
|
735
917
|
return freeze(result);
|
|
736
918
|
}
|
|
@@ -750,7 +932,9 @@ async function loadTeamPlan(path, options = {}) {
|
|
|
750
932
|
if (length > MAX_PLAN_BYTES) fail("Team plan exceeds 1 MiB");
|
|
751
933
|
let value;
|
|
752
934
|
try {
|
|
753
|
-
value = JSON.parse(
|
|
935
|
+
value = JSON.parse(
|
|
936
|
+
new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, length)).replace(/^\uFEFF/u, "")
|
|
937
|
+
);
|
|
754
938
|
} catch {
|
|
755
939
|
fail("Team plan must contain valid UTF-8 JSON");
|
|
756
940
|
}
|
|
@@ -765,55 +949,75 @@ function createTeamPreset(preset, prompt, options = {}) {
|
|
|
765
949
|
text(prompt, "team prompt", 32768);
|
|
766
950
|
const coding = options.mode === "coding";
|
|
767
951
|
const worker = {
|
|
768
|
-
...
|
|
952
|
+
...omitUndefined({ model: options.model }),
|
|
769
953
|
tools: coding ? [...READ_TOOLS, "write", "edit"] : READ_TOOLS,
|
|
770
954
|
instructions: coding ? "Implement a bounded change in your own workspace copy. Run only configured checks. Submit evidence and a report; never claim source changes have been applied." : "Investigate independently using workspace evidence. Do not change files or claim unperformed tests. Submit findings, evidence, and limitations."
|
|
771
955
|
};
|
|
772
956
|
const final = {
|
|
773
|
-
...
|
|
957
|
+
...omitUndefined({ model: options.model }),
|
|
774
958
|
tools: READ_TOOLS,
|
|
775
959
|
delegateTo: preset === "supervisor" ? ["worker"] : [],
|
|
776
960
|
instructions: "Synthesize the dependency reports, identify each task's contribution, and distinguish verified evidence from unverified claims. Dependency workspaces are not automatically merged. Do not wait for unsolicited messages."
|
|
777
961
|
};
|
|
778
962
|
const restriction = coding ? "Work only in your isolated copy; source application requires later human confirmation." : "Do not change files.";
|
|
779
963
|
const tasks = preset === "pipeline" ? [
|
|
780
|
-
{
|
|
964
|
+
{
|
|
965
|
+
id: "analysis",
|
|
966
|
+
agent: "worker",
|
|
967
|
+
input: `Inspect the request and produce evidence and a concrete ${coding ? "implementation" : "investigation"}. ${restriction}
|
|
781
968
|
|
|
782
969
|
User task:
|
|
783
|
-
${prompt}`
|
|
784
|
-
|
|
970
|
+
${prompt}`
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
id: "review",
|
|
974
|
+
agent: "worker",
|
|
975
|
+
dependsOn: ["analysis"],
|
|
976
|
+
input: `Review the analysis report for correctness, edge cases, and verification gaps. Your workspace is an independent baseline; no prior edits are present. ${restriction}
|
|
785
977
|
|
|
786
978
|
User task:
|
|
787
|
-
${prompt}`
|
|
979
|
+
${prompt}`
|
|
980
|
+
},
|
|
788
981
|
{ id: "summary", agent: "supervisor", dependsOn: ["analysis", "review"], input: prompt }
|
|
789
982
|
] : [
|
|
790
|
-
{
|
|
983
|
+
{
|
|
984
|
+
id: "analysis",
|
|
985
|
+
agent: "worker",
|
|
986
|
+
input: `Investigate the user's task independently. Inspect relevant files and identify a concrete approach with evidence. ${restriction}
|
|
791
987
|
|
|
792
988
|
User task:
|
|
793
|
-
${prompt}`
|
|
794
|
-
|
|
989
|
+
${prompt}`
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
id: "review",
|
|
993
|
+
agent: "worker",
|
|
994
|
+
input: `Independently review risks, edge cases, and validation needs. Inspect files and provide evidence. ${restriction}
|
|
795
995
|
|
|
796
996
|
User task:
|
|
797
|
-
${prompt}`
|
|
997
|
+
${prompt}`
|
|
998
|
+
},
|
|
798
999
|
{ id: "summary", agent: "supervisor", dependsOn: ["analysis", "review"], input: prompt }
|
|
799
1000
|
];
|
|
800
|
-
return parseTeamPlan(
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1001
|
+
return parseTeamPlan(
|
|
1002
|
+
{
|
|
1003
|
+
format: 1,
|
|
1004
|
+
name: preset,
|
|
1005
|
+
roles: { worker, supervisor: final },
|
|
1006
|
+
tasks,
|
|
1007
|
+
resultTaskId: "summary",
|
|
1008
|
+
limits: {
|
|
1009
|
+
maxConcurrent: 2,
|
|
1010
|
+
maxTasks: 8,
|
|
1011
|
+
maxDepth: 2,
|
|
1012
|
+
maxTaskTurns: 4,
|
|
1013
|
+
maxDurationMs: 6e5,
|
|
1014
|
+
maxOutputBytes: 65536,
|
|
1015
|
+
maxMessages: 24,
|
|
1016
|
+
maxMessageBytes: 8192
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
{ mode: options.mode ?? "read-only" }
|
|
1020
|
+
);
|
|
817
1021
|
}
|
|
818
1022
|
__name(createTeamPreset, "createTeamPreset");
|
|
819
1023
|
function parseLimits(value) {
|
|
@@ -825,7 +1029,8 @@ function parseLimits(value) {
|
|
|
825
1029
|
continue;
|
|
826
1030
|
}
|
|
827
1031
|
const cap = LIMIT_CAPS[key];
|
|
828
|
-
if (typeof value2 !== "number" || !Number.isSafeInteger(value2) || value2 < 1 || value2 > cap)
|
|
1032
|
+
if (typeof value2 !== "number" || !Number.isSafeInteger(value2) || value2 < 1 || value2 > cap)
|
|
1033
|
+
fail(`plan.limits.${key} must be an integer from 1 to ${cap}`);
|
|
829
1034
|
result[key] = value2;
|
|
830
1035
|
}
|
|
831
1036
|
return result;
|
|
@@ -843,7 +1048,8 @@ __name(budget, "budget");
|
|
|
843
1048
|
function validateGraph(tasks) {
|
|
844
1049
|
const pending = new Map(tasks.map((task) => [task.id, task]));
|
|
845
1050
|
if (pending.size !== tasks.length) fail("Duplicate task id");
|
|
846
|
-
for (const task of tasks)
|
|
1051
|
+
for (const task of tasks)
|
|
1052
|
+
for (const id of task.dependsOn ?? []) if (!pending.has(id)) fail(`Unknown dependency ${id} in task ${task.id}`);
|
|
847
1053
|
while (pending.size) {
|
|
848
1054
|
let progress = false;
|
|
849
1055
|
for (const [id, task] of pending) {
|
|
@@ -856,7 +1062,8 @@ function validateGraph(tasks) {
|
|
|
856
1062
|
}
|
|
857
1063
|
__name(validateGraph, "validateGraph");
|
|
858
1064
|
function object(value, label, keys) {
|
|
859
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || ![null, Object.prototype].includes(Object.getPrototypeOf(value)))
|
|
1065
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![null, Object.prototype].includes(Object.getPrototypeOf(value)))
|
|
1066
|
+
fail(`${label} must be an object`);
|
|
860
1067
|
const record = value;
|
|
861
1068
|
if (keys) {
|
|
862
1069
|
for (const key of Object.keys(record)) if (!keys.includes(key)) fail(`Unknown ${label} field: ${key}`);
|
|
@@ -865,17 +1072,20 @@ function object(value, label, keys) {
|
|
|
865
1072
|
}
|
|
866
1073
|
__name(object, "object");
|
|
867
1074
|
function identifier2(value, label) {
|
|
868
|
-
if (typeof value !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/u.test(value) || ["__proto__", "constructor", "prototype"].includes(value))
|
|
1075
|
+
if (typeof value !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/u.test(value) || ["__proto__", "constructor", "prototype"].includes(value))
|
|
1076
|
+
fail(`${label} must be a non-reserved 1-128 character ASCII identifier`);
|
|
869
1077
|
return value;
|
|
870
1078
|
}
|
|
871
1079
|
__name(identifier2, "identifier");
|
|
872
1080
|
function text(value, label, maxBytes) {
|
|
873
|
-
if (typeof value !== "string" || !value.trim() || value.includes("\0") || Buffer.byteLength(value, "utf8") > maxBytes)
|
|
1081
|
+
if (typeof value !== "string" || !value.trim() || value.includes("\0") || Buffer.byteLength(value, "utf8") > maxBytes)
|
|
1082
|
+
fail(`${label} must be nonempty text of at most ${maxBytes} bytes without NUL`);
|
|
874
1083
|
return value;
|
|
875
1084
|
}
|
|
876
1085
|
__name(text, "text");
|
|
877
1086
|
function stringArray(value, label, max) {
|
|
878
|
-
if (!Array.isArray(value) || value.length > max || value.some((item) => typeof item !== "string") || new Set(value).size !== value.length)
|
|
1087
|
+
if (!Array.isArray(value) || value.length > max || value.some((item) => typeof item !== "string") || new Set(value).size !== value.length)
|
|
1088
|
+
fail(`${label} must be an array of at most ${max} unique strings`);
|
|
879
1089
|
return [...value];
|
|
880
1090
|
}
|
|
881
1091
|
__name(stringArray, "stringArray");
|
|
@@ -896,12 +1106,13 @@ __name(fail, "fail");
|
|
|
896
1106
|
import { createHash as createHash2 } from "node:crypto";
|
|
897
1107
|
import { open as open3 } from "node:fs/promises";
|
|
898
1108
|
import { join as join2 } from "node:path";
|
|
899
|
-
import { FileSessionStore } from "@sortedyuan/session/file-store";
|
|
1109
|
+
import { FileSessionStore } from "@sortedyuan/may/session/file-store";
|
|
900
1110
|
async function readTeamResolution(path) {
|
|
901
1111
|
const file = await open3(path, "r");
|
|
902
1112
|
let value;
|
|
903
1113
|
try {
|
|
904
|
-
if (!(await file.stat()).isFile() || (await file.stat()).size > 32768)
|
|
1114
|
+
if (!(await file.stat()).isFile() || (await file.stat()).size > 32768)
|
|
1115
|
+
throw new Error("Resolution file exceeds 32768 bytes");
|
|
905
1116
|
const bytes = await file.readFile();
|
|
906
1117
|
if (bytes.length > 32768) throw new Error("Resolution file exceeds 32768 bytes");
|
|
907
1118
|
value = JSON.parse(bytes.toString("utf8"));
|
|
@@ -910,22 +1121,28 @@ async function readTeamResolution(path) {
|
|
|
910
1121
|
}
|
|
911
1122
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Resolution must be an object");
|
|
912
1123
|
const input = value;
|
|
913
|
-
if (input.format !== 1 || !["task", "budget", "check"].includes(input.kind) || typeof input.finding !== "string" || !input.finding.trim() || input.finding.length > (input.kind === "budget" ? 256 : 8192) || /[\u0000-\u001f]/u.test(input.finding))
|
|
1124
|
+
if (input.format !== 1 || !["task", "budget", "check"].includes(input.kind) || typeof input.finding !== "string" || !input.finding.trim() || input.finding.length > (input.kind === "budget" ? 256 : 8192) || /[\u0000-\u001f]/u.test(input.finding))
|
|
1125
|
+
throw new Error("Resolution requires format 1, a supported kind, and bounded verified finding");
|
|
914
1126
|
const keys = input.kind === "budget" ? ["callId", "usage"] : [input.kind === "task" ? "taskId" : "commandId", "outcome"];
|
|
915
|
-
if (Object.keys(input).some((key) => !["format", "kind", "finding", ...keys].includes(key)))
|
|
1127
|
+
if (Object.keys(input).some((key) => !["format", "kind", "finding", ...keys].includes(key)))
|
|
1128
|
+
throw new Error("Unknown resolution field");
|
|
916
1129
|
const id = input[keys[0]];
|
|
917
|
-
if (typeof id !== "string" || !id.length || id.length > 256 || /[\u0000-\u001f]/u.test(id))
|
|
1130
|
+
if (typeof id !== "string" || !id.length || id.length > 256 || /[\u0000-\u001f]/u.test(id))
|
|
1131
|
+
throw new Error("Invalid resolution identity");
|
|
918
1132
|
if (input.kind === "budget") {
|
|
919
1133
|
const usage = input.usage;
|
|
920
|
-
if (!usage || typeof usage !== "object" || Array.isArray(usage) || Object.keys(usage).some((key) => !["inputTokens", "outputTokens", "totalTokens"].includes(key)) || !Object.keys(usage).length || Object.values(usage).some((n) => !Number.isSafeInteger(n) || n < 0) || usage.totalTokens === void 0 && (usage.inputTokens === void 0 || usage.outputTokens === void 0))
|
|
921
|
-
|
|
1134
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage) || Object.keys(usage).some((key) => !["inputTokens", "outputTokens", "totalTokens"].includes(key)) || !Object.keys(usage).length || Object.values(usage).some((n) => !Number.isSafeInteger(n) || n < 0) || usage.totalTokens === void 0 && (usage.inputTokens === void 0 || usage.outputTokens === void 0))
|
|
1135
|
+
throw new Error("Supply verified non-negative token usage; do not estimate unknown usage as zero");
|
|
1136
|
+
} else if (!["failed", "cancelled"].includes(input.outcome))
|
|
1137
|
+
throw new Error("Reconciliation cannot manufacture a successful answer/check");
|
|
922
1138
|
return input;
|
|
923
1139
|
}
|
|
924
1140
|
__name(readTeamResolution, "readTeamResolution");
|
|
925
1141
|
function teamReviewDigest(value) {
|
|
926
1142
|
const canonical = /* @__PURE__ */ __name((value2) => {
|
|
927
1143
|
if (Array.isArray(value2)) return `[${value2.map(canonical).join(",")}]`;
|
|
928
|
-
if (value2 && typeof value2 === "object")
|
|
1144
|
+
if (value2 && typeof value2 === "object")
|
|
1145
|
+
return `{${Object.entries(value2).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(",")}}`;
|
|
929
1146
|
return JSON.stringify(value2);
|
|
930
1147
|
}, "canonical");
|
|
931
1148
|
return createHash2("sha256").update(canonical(value)).digest("hex");
|
|
@@ -938,10 +1155,11 @@ function retryImpact(state, taskId, finding) {
|
|
|
938
1155
|
const affected = /* @__PURE__ */ new Set([taskId]);
|
|
939
1156
|
for (let changed = true; changed; ) {
|
|
940
1157
|
changed = false;
|
|
941
|
-
for (const candidate of state.tasks)
|
|
942
|
-
affected.
|
|
943
|
-
|
|
944
|
-
|
|
1158
|
+
for (const candidate of state.tasks)
|
|
1159
|
+
if (!affected.has(candidate.id) && candidate.dependsOn.some((id) => affected.has(id))) {
|
|
1160
|
+
affected.add(candidate.id);
|
|
1161
|
+
changed = true;
|
|
1162
|
+
}
|
|
945
1163
|
}
|
|
946
1164
|
return {
|
|
947
1165
|
taskId,
|
|
@@ -967,23 +1185,31 @@ async function inspectTeam(directory, id, limits) {
|
|
|
967
1185
|
for (const task of state.tasks) {
|
|
968
1186
|
lines2.push(`${task.id}: ${task.status}; attempt ${task.attempt ?? 0}; session ${task.sessionId}`);
|
|
969
1187
|
if (task.detail) lines2.push(` Reason: ${task.detail}`);
|
|
970
|
-
const blocked = task.dependsOn.filter(
|
|
1188
|
+
const blocked = task.dependsOn.filter(
|
|
1189
|
+
(id2) => state.tasks.find((candidate) => candidate.id === id2)?.status !== "completed"
|
|
1190
|
+
);
|
|
971
1191
|
if (blocked.length) lines2.push(` Unsuccessful dependencies: ${blocked.join(", ")}`);
|
|
972
1192
|
if (["failed", "cancelled", "recovery-required", "cancelling"].includes(task.status)) {
|
|
973
1193
|
const events = await sessions.read(task.sessionId);
|
|
974
1194
|
const pending = /* @__PURE__ */ new Map();
|
|
975
1195
|
for (const event of events) {
|
|
976
|
-
if (event.type === "tool.started")
|
|
977
|
-
|
|
1196
|
+
if (event.type === "tool.started")
|
|
1197
|
+
pending.set(`${event.runId}:${event.step}:${event.call.id}`, event.call.name);
|
|
1198
|
+
if (event.type === "tool.completed" || event.type === "tool.failed" && !/CANCEL|ABORT|UNKNOWN/iu.test(`${event.error.name} ${event.error.code ?? ""}`))
|
|
1199
|
+
pending.delete(`${event.runId}:${event.step}:${event.call.id}`);
|
|
978
1200
|
}
|
|
979
|
-
for (const [key, name] of pending)
|
|
1201
|
+
for (const [key, name] of pending)
|
|
1202
|
+
lines2.push(` Unsettled tool evidence: ${key} (${name}); inspect Session before reconciling task`);
|
|
980
1203
|
}
|
|
981
1204
|
}
|
|
982
1205
|
}
|
|
983
1206
|
if (budget2) lines2.push(...budgetStatus(budget2));
|
|
984
1207
|
if (verification) {
|
|
985
1208
|
lines2.push(`Structured reports: ${verification.reports.length}; checks: ${verification.checks.length}`);
|
|
986
|
-
for (const check of verification.checks)
|
|
1209
|
+
for (const check of verification.checks)
|
|
1210
|
+
lines2.push(
|
|
1211
|
+
` Check ${check.spec.id} (${check.taskId}): ${check.status}; command ${check.commandId}${check.detail ? `; ${check.detail}` : ""}`
|
|
1212
|
+
);
|
|
987
1213
|
}
|
|
988
1214
|
lines2.push(
|
|
989
1215
|
"Recovery does not steal locks, erase unknown effects, reset budgets, or execute agents.",
|
|
@@ -994,7 +1220,12 @@ async function inspectTeam(directory, id, limits) {
|
|
|
994
1220
|
}
|
|
995
1221
|
__name(inspectTeam, "inspectTeam");
|
|
996
1222
|
function budgetStatus(budget2) {
|
|
997
|
-
return [
|
|
1223
|
+
return [
|
|
1224
|
+
`Shared budget: ${JSON.stringify(sharedBudgetTotals(budget2))}`,
|
|
1225
|
+
...budget2.calls.filter((call) => call.status !== "settled" || call.exceededReservation).map(
|
|
1226
|
+
(call) => ` Budget call ${call.id}: ${call.status}${call.exceededReservation ? ", exceeded reservation" : ""}; reserved ${call.reservation.totalTokens} tokens`
|
|
1227
|
+
)
|
|
1228
|
+
];
|
|
998
1229
|
}
|
|
999
1230
|
__name(budgetStatus, "budgetStatus");
|
|
1000
1231
|
async function optional(read) {
|
|
@@ -1008,7 +1239,7 @@ async function optional(read) {
|
|
|
1008
1239
|
__name(optional, "optional");
|
|
1009
1240
|
|
|
1010
1241
|
// apps/maybecode/src/team-patches.ts
|
|
1011
|
-
import { acquireFileLock as acquireFileLock2,
|
|
1242
|
+
import { acquireFileLock as acquireFileLock2, hasErrorCode as hasErrorCode2 } from "@sortedyuan/may/storage";
|
|
1012
1243
|
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
1013
1244
|
import { constants as constants2 } from "node:fs";
|
|
1014
1245
|
import { lstat as lstat2, mkdir as mkdir2, open as open4, realpath as realpath2, rename, unlink } from "node:fs/promises";
|
|
@@ -1020,15 +1251,17 @@ var MAX_FILE_BYTES = 262144;
|
|
|
1020
1251
|
var MAX_TOTAL_BYTES = 2097152;
|
|
1021
1252
|
async function createTeamPatch(options) {
|
|
1022
1253
|
const bundle = bundleFrom(await options.workspaces.snapshotPatch(options.taskIds));
|
|
1023
|
-
if (inside(bundle.sourceDirectory, resolve2(options.directory)))
|
|
1254
|
+
if (inside(bundle.sourceDirectory, resolve2(options.directory)))
|
|
1255
|
+
throw new Error("Patch storage must be outside the source workspace");
|
|
1024
1256
|
const patches = await privateDirectory(join3(resolve2(options.directory), "patches"));
|
|
1025
1257
|
const path = join3(patches, `${bundle.id}.json`);
|
|
1026
1258
|
try {
|
|
1027
1259
|
await durableFile(path, `${JSON.stringify(bundle, null, 2)}
|
|
1028
1260
|
`, "wx");
|
|
1029
1261
|
} catch (error) {
|
|
1030
|
-
if (!
|
|
1031
|
-
if (!isDeepStrictEqual2(await readTeamPatch(options.directory, bundle.id), bundle))
|
|
1262
|
+
if (!hasErrorCode2(error, "EEXIST")) throw error;
|
|
1263
|
+
if (!isDeepStrictEqual2(await readTeamPatch(options.directory, bundle.id), bundle))
|
|
1264
|
+
throw new Error("Patch id already exists with different content");
|
|
1032
1265
|
}
|
|
1033
1266
|
return bundle;
|
|
1034
1267
|
}
|
|
@@ -1045,11 +1278,16 @@ async function readTeamPatch(directory, patchId) {
|
|
|
1045
1278
|
__name(readTeamPatch, "readTeamPatch");
|
|
1046
1279
|
function renderTeamPatchDiff(bundle) {
|
|
1047
1280
|
validateBundle(bundle, bundle.id);
|
|
1048
|
-
const sections = [
|
|
1281
|
+
const sections = [
|
|
1282
|
+
`Patch ${bundle.id}`,
|
|
1283
|
+
`Confirm digest: ${bundle.digest}`,
|
|
1284
|
+
`Tasks: ${bundle.taskSnapshots.map((task) => task.taskId).join(", ")}`
|
|
1285
|
+
];
|
|
1049
1286
|
for (const file of bundle.files) sections.push(fileDiff(file));
|
|
1050
1287
|
for (const conflict of bundle.conflicts) {
|
|
1051
1288
|
sections.push(`CONFLICT ${conflict.path}: different task results; apply is blocked.`);
|
|
1052
|
-
for (const file of conflict.candidates)
|
|
1289
|
+
for (const file of conflict.candidates)
|
|
1290
|
+
sections.push(`Candidate tasks: ${file.taskIds.join(", ")}
|
|
1053
1291
|
${fileDiff(file)}`);
|
|
1054
1292
|
}
|
|
1055
1293
|
if (bundle.files.length === 0 && bundle.conflicts.length === 0) sections.push("No source changes.");
|
|
@@ -1059,8 +1297,10 @@ ${fileDiff(file)}`);
|
|
|
1059
1297
|
__name(renderTeamPatchDiff, "renderTeamPatchDiff");
|
|
1060
1298
|
async function applyTeamPatch(options) {
|
|
1061
1299
|
const bundle = await readTeamPatch(options.directory, options.patchId);
|
|
1062
|
-
if (options.confirmDigest !== bundle.digest)
|
|
1063
|
-
|
|
1300
|
+
if (options.confirmDigest !== bundle.digest)
|
|
1301
|
+
throw new Error("Apply requires the exact digest from the reviewed patch");
|
|
1302
|
+
if (bundle.conflicts.length > 0)
|
|
1303
|
+
throw new Error("Patch has conflicting task results; choose a non-conflicting task set");
|
|
1064
1304
|
const existing = await readTeamPatchApplication(options.directory, bundle.id);
|
|
1065
1305
|
if (existing) return { ...existing, ...existing.status === "applied" ? { alreadyApplied: true } : {} };
|
|
1066
1306
|
const source = await safeRoot(bundle.sourceDirectory);
|
|
@@ -1068,16 +1308,17 @@ async function applyTeamPatch(options) {
|
|
|
1068
1308
|
const taskIds = bundle.taskSnapshots.map((task) => task.taskId);
|
|
1069
1309
|
const checkTasks = /* @__PURE__ */ __name(async () => {
|
|
1070
1310
|
const current = bundleFrom(await options.workspaces.snapshotPatch(taskIds));
|
|
1071
|
-
if (current.digest !== bundle.digest)
|
|
1311
|
+
if (current.digest !== bundle.digest)
|
|
1312
|
+
throw new Error("Task or baseline snapshot changed since review; create a new patch");
|
|
1072
1313
|
}, "checkTasks");
|
|
1073
1314
|
await checkTasks();
|
|
1074
1315
|
await preflight(source, bundle.files);
|
|
1075
1316
|
const lockDirectory = join3(homedir(), ".may", "maybecode", "apply-locks");
|
|
1076
|
-
if (inside(source, resolve2(lockDirectory)))
|
|
1317
|
+
if (inside(source, resolve2(lockDirectory)))
|
|
1318
|
+
throw new Error("Apply lock directory must be outside the source workspace");
|
|
1077
1319
|
const locks = await privateDirectory(lockDirectory);
|
|
1078
1320
|
if (inside(source, locks)) throw new Error("Apply lock directory must be outside the source workspace");
|
|
1079
|
-
const
|
|
1080
|
-
const lock = await acquireFileLock2(lockPath);
|
|
1321
|
+
const lock = await acquireFileLock2(join3(locks, `${hash2(source)}.lock`));
|
|
1081
1322
|
let journal;
|
|
1082
1323
|
let sequence = 0;
|
|
1083
1324
|
const appliedPaths = [];
|
|
@@ -1091,9 +1332,6 @@ async function applyTeamPatch(options) {
|
|
|
1091
1332
|
sequence++;
|
|
1092
1333
|
}, "append");
|
|
1093
1334
|
try {
|
|
1094
|
-
await lock.writeFile(`${JSON.stringify({ pid: process.pid, patchId: bundle.id, source })}
|
|
1095
|
-
`);
|
|
1096
|
-
await lock.sync();
|
|
1097
1335
|
const prior = await readTeamPatchApplication(options.directory, bundle.id);
|
|
1098
1336
|
if (prior) return { ...prior, ...prior.status === "applied" ? { alreadyApplied: true } : {} };
|
|
1099
1337
|
await checkTasks();
|
|
@@ -1157,18 +1395,30 @@ async function applyTeamPatch(options) {
|
|
|
1157
1395
|
}
|
|
1158
1396
|
await syncDirectory(dirname(target));
|
|
1159
1397
|
const after = await safeRead(source, target, MAX_FILE_BYTES);
|
|
1160
|
-
if (file.afterSha256 === void 0 ? after !== void 0 : after === void 0 || hash2(after) !== file.afterSha256)
|
|
1398
|
+
if (file.afterSha256 === void 0 ? after !== void 0 : after === void 0 || hash2(after) !== file.afterSha256)
|
|
1399
|
+
throw new Error("Source changed before the patch result was acknowledged");
|
|
1161
1400
|
await append({ type: "applied", path: file.path });
|
|
1162
1401
|
appliedPaths.push(file.path);
|
|
1163
1402
|
options.onProgress?.({ type: "file.applied", path: file.path });
|
|
1164
1403
|
}
|
|
1165
1404
|
await append({ type: "completed" });
|
|
1166
|
-
return {
|
|
1405
|
+
return {
|
|
1406
|
+
status: "applied",
|
|
1407
|
+
patchId: bundle.id,
|
|
1408
|
+
digest: bundle.digest,
|
|
1409
|
+
appliedPaths,
|
|
1410
|
+
uncertainPaths: [],
|
|
1411
|
+
journalPath
|
|
1412
|
+
};
|
|
1167
1413
|
} catch (error) {
|
|
1168
1414
|
if (!begun) throw error;
|
|
1169
1415
|
try {
|
|
1170
1416
|
await append({ type: "unknown" });
|
|
1171
|
-
} catch {
|
|
1417
|
+
} catch (appendError) {
|
|
1418
|
+
throw new AggregateError(
|
|
1419
|
+
[error, appendError],
|
|
1420
|
+
"Patch application failed and its unknown outcome could not be recorded"
|
|
1421
|
+
);
|
|
1172
1422
|
}
|
|
1173
1423
|
return {
|
|
1174
1424
|
status: "unknown",
|
|
@@ -1183,7 +1433,7 @@ async function applyTeamPatch(options) {
|
|
|
1183
1433
|
try {
|
|
1184
1434
|
await journal?.close();
|
|
1185
1435
|
} finally {
|
|
1186
|
-
await
|
|
1436
|
+
await lock.release();
|
|
1187
1437
|
}
|
|
1188
1438
|
}
|
|
1189
1439
|
}
|
|
@@ -1203,9 +1453,11 @@ async function readTeamPatchApplication(directory, patchId) {
|
|
|
1203
1453
|
for (const line of bytes.toString("utf8").split("\n").slice(0, -1)) {
|
|
1204
1454
|
try {
|
|
1205
1455
|
const entry = JSON.parse(line);
|
|
1206
|
-
if (entry.revision !== ++revision || entry.digest !== bundle.digest || !["started", "intent", "applied", "completed", "unknown"].includes(entry.type) || (revision === 1 ? entry.type !== "started" : entry.type === "started") || ["completed", "unknown"].includes(last ?? ""))
|
|
1456
|
+
if (entry.revision !== ++revision || entry.digest !== bundle.digest || !["started", "intent", "applied", "completed", "unknown"].includes(entry.type) || (revision === 1 ? entry.type !== "started" : entry.type === "started") || ["completed", "unknown"].includes(last ?? ""))
|
|
1457
|
+
throw new Error("Invalid application sequence");
|
|
1207
1458
|
if (entry.type === "intent") {
|
|
1208
|
-
if (!bundle.files.some((file) => file.path === entry.path) || pending.has(entry.path) || appliedPaths.includes(entry.path))
|
|
1459
|
+
if (!bundle.files.some((file) => file.path === entry.path) || pending.has(entry.path) || appliedPaths.includes(entry.path))
|
|
1460
|
+
throw new Error("Invalid application intent");
|
|
1209
1461
|
pending.add(entry.path);
|
|
1210
1462
|
}
|
|
1211
1463
|
if (entry.type === "applied") {
|
|
@@ -1213,7 +1465,8 @@ async function readTeamPatchApplication(directory, patchId) {
|
|
|
1213
1465
|
appliedPaths.push(entry.path);
|
|
1214
1466
|
}
|
|
1215
1467
|
if (entry.type === "completed") {
|
|
1216
|
-
if (pending.size || appliedPaths.length !== bundle.files.length)
|
|
1468
|
+
if (pending.size || appliedPaths.length !== bundle.files.length)
|
|
1469
|
+
throw new Error("Application completed before all files");
|
|
1217
1470
|
completed = true;
|
|
1218
1471
|
}
|
|
1219
1472
|
last = entry.type;
|
|
@@ -1239,7 +1492,9 @@ function bundleFrom(snapshot) {
|
|
|
1239
1492
|
for (const { taskId, ...change } of snapshot.changes) {
|
|
1240
1493
|
assertRelativePath(change.path);
|
|
1241
1494
|
const group = groups.get(change.path) ?? [];
|
|
1242
|
-
const identical = group.find(
|
|
1495
|
+
const identical = group.find(
|
|
1496
|
+
(file) => isDeepStrictEqual2({ ...file, taskIds: void 0 }, { ...change, taskIds: void 0 })
|
|
1497
|
+
);
|
|
1243
1498
|
if (identical) group[group.indexOf(identical)] = { ...identical, taskIds: [...identical.taskIds, taskId].sort() };
|
|
1244
1499
|
else group.push({ ...change, taskIds: [taskId] });
|
|
1245
1500
|
groups.set(change.path, group);
|
|
@@ -1271,12 +1526,14 @@ function bundleFrom(snapshot) {
|
|
|
1271
1526
|
__name(bundleFrom, "bundleFrom");
|
|
1272
1527
|
function validateBundle(bundle, expectedId) {
|
|
1273
1528
|
assertPatchId(expectedId);
|
|
1274
|
-
if (!bundle || bundle.format !== 1 || bundle.id !== expectedId || bundle.id !== `patch-${bundle.digest}` || !isAbsolute2(bundle.sourceDirectory) || !/^[a-f0-9]{64}$/u.test(bundle.baselineDigest) || !Array.isArray(bundle.taskSnapshots) || !bundle.taskSnapshots.length || !Array.isArray(bundle.files) || !Array.isArray(bundle.conflicts))
|
|
1529
|
+
if (!bundle || bundle.format !== 1 || bundle.id !== expectedId || bundle.id !== `patch-${bundle.digest}` || !isAbsolute2(bundle.sourceDirectory) || !/^[a-f0-9]{64}$/u.test(bundle.baselineDigest) || !Array.isArray(bundle.taskSnapshots) || !bundle.taskSnapshots.length || !Array.isArray(bundle.files) || !Array.isArray(bundle.conflicts))
|
|
1530
|
+
throw new Error("Invalid patch bundle");
|
|
1275
1531
|
const { id: _id, digest, ...body } = bundle;
|
|
1276
1532
|
if (hash2(JSON.stringify(body)) !== digest) throw new Error("Patch digest does not match its content");
|
|
1277
1533
|
const ids = /* @__PURE__ */ new Set();
|
|
1278
1534
|
for (const task of bundle.taskSnapshots) {
|
|
1279
|
-
if (typeof task.taskId !== "string" || !task.taskId || ids.has(task.taskId) || !/^[a-f0-9]{64}$/u.test(task.digest))
|
|
1535
|
+
if (typeof task.taskId !== "string" || !task.taskId || ids.has(task.taskId) || !/^[a-f0-9]{64}$/u.test(task.digest))
|
|
1536
|
+
throw new Error("Invalid patch task identity");
|
|
1280
1537
|
ids.add(task.taskId);
|
|
1281
1538
|
}
|
|
1282
1539
|
let total = 0;
|
|
@@ -1284,12 +1541,14 @@ function validateBundle(bundle, expectedId) {
|
|
|
1284
1541
|
if (records.length > 128) throw new Error("Patch has too many file records");
|
|
1285
1542
|
for (const file of records) {
|
|
1286
1543
|
assertRelativePath(file.path);
|
|
1287
|
-
if (!["added", "modified", "deleted"].includes(file.kind) || !Array.isArray(file.taskIds) || !file.taskIds.length || file.taskIds.some((id) => typeof id !== "string" || !ids.has(id)))
|
|
1544
|
+
if (!["added", "modified", "deleted"].includes(file.kind) || !Array.isArray(file.taskIds) || !file.taskIds.length || file.taskIds.some((id) => typeof id !== "string" || !ids.has(id)))
|
|
1545
|
+
throw new Error("Invalid patch file ownership");
|
|
1288
1546
|
for (const prefix of ["before", "after"]) {
|
|
1289
1547
|
const text2 = file[`${prefix}Text`];
|
|
1290
1548
|
const sha = file[`${prefix}Sha256`];
|
|
1291
1549
|
const expected = prefix === "before" ? file.kind !== "added" : file.kind !== "deleted";
|
|
1292
|
-
if (expected ? typeof text2 !== "string" || sha !== hash2(text2) || /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u.test(text2) : text2 !== void 0 || sha !== void 0)
|
|
1550
|
+
if (expected ? typeof text2 !== "string" || sha !== hash2(text2) || /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u.test(text2) : text2 !== void 0 || sha !== void 0)
|
|
1551
|
+
throw new Error("Invalid patch file content/hash or unsafe display controls");
|
|
1293
1552
|
if (text2 !== void 0) {
|
|
1294
1553
|
const bytes = Buffer.byteLength(text2);
|
|
1295
1554
|
if (bytes > MAX_FILE_BYTES) throw new Error("Patch file exceeds its byte limit");
|
|
@@ -1309,7 +1568,10 @@ function fileDiff(file) {
|
|
|
1309
1568
|
`+++ ${file.afterText === void 0 ? "/dev/null" : `b/${file.path}`}`,
|
|
1310
1569
|
`@@ -${old.length ? 1 : 0},${old.length} +${next.length ? 1 : 0},${next.length} @@`
|
|
1311
1570
|
];
|
|
1312
|
-
for (const [prefix, entries, text2] of [
|
|
1571
|
+
for (const [prefix, entries, text2] of [
|
|
1572
|
+
["-", old, file.beforeText],
|
|
1573
|
+
["+", next, file.afterText]
|
|
1574
|
+
]) {
|
|
1313
1575
|
for (const line of entries) rendered.push(`${prefix}${line}`);
|
|
1314
1576
|
if (entries.length && !text2.endsWith("\n")) rendered.push("\");
|
|
1315
1577
|
}
|
|
@@ -1326,7 +1588,9 @@ __name(lines, "lines");
|
|
|
1326
1588
|
async function preflight(source, files) {
|
|
1327
1589
|
const originals = /* @__PURE__ */ new Map();
|
|
1328
1590
|
const paths = files.map((file) => file.path);
|
|
1329
|
-
for (const path of paths)
|
|
1591
|
+
for (const path of paths)
|
|
1592
|
+
if (paths.some((other) => other !== path && other.startsWith(`${path}/`)))
|
|
1593
|
+
throw new Error("Patch contains overlapping file and directory paths");
|
|
1330
1594
|
for (const file of files) {
|
|
1331
1595
|
const target = checkedPath(source, file.path);
|
|
1332
1596
|
const current = await safeRead(source, target, MAX_FILE_BYTES);
|
|
@@ -1338,7 +1602,8 @@ async function preflight(source, files) {
|
|
|
1338
1602
|
}
|
|
1339
1603
|
__name(preflight, "preflight");
|
|
1340
1604
|
function assertBefore(file, current) {
|
|
1341
|
-
if (file.beforeSha256 === void 0 ? current !== void 0 : current === void 0 || hash2(current) !== file.beforeSha256)
|
|
1605
|
+
if (file.beforeSha256 === void 0 ? current !== void 0 : current === void 0 || hash2(current) !== file.beforeSha256)
|
|
1606
|
+
throw new Error(`Source changed since the baseline: ${file.path}`);
|
|
1342
1607
|
}
|
|
1343
1608
|
__name(assertBefore, "assertBefore");
|
|
1344
1609
|
async function safeRead(root, path, maxBytes) {
|
|
@@ -1347,14 +1612,16 @@ async function safeRead(root, path, maxBytes) {
|
|
|
1347
1612
|
try {
|
|
1348
1613
|
before = await lstat2(path);
|
|
1349
1614
|
} catch (error) {
|
|
1350
|
-
if (
|
|
1615
|
+
if (hasErrorCode2(error, "ENOENT")) return void 0;
|
|
1351
1616
|
throw error;
|
|
1352
1617
|
}
|
|
1353
|
-
if (!before.isFile() || before.isSymbolicLink() || before.nlink > 1 || before.size > maxBytes)
|
|
1618
|
+
if (!before.isFile() || before.isSymbolicLink() || before.nlink > 1 || before.size > maxBytes)
|
|
1619
|
+
throw new Error("Patch file is unsafe or exceeds its byte limit");
|
|
1354
1620
|
const file = await open4(path, constants2.O_RDONLY | (process.platform === "win32" ? 0 : constants2.O_NOFOLLOW));
|
|
1355
1621
|
try {
|
|
1356
1622
|
const stat2 = await file.stat();
|
|
1357
|
-
if (!stat2.isFile() || stat2.ino !== before.ino || stat2.dev !== before.dev || stat2.size !== before.size || stat2.nlink > 1)
|
|
1623
|
+
if (!stat2.isFile() || stat2.ino !== before.ino || stat2.dev !== before.dev || stat2.size !== before.size || stat2.nlink > 1)
|
|
1624
|
+
throw new Error("Patch file changed while opening");
|
|
1358
1625
|
const bytes = Buffer.alloc(before.size + 1);
|
|
1359
1626
|
let position = 0;
|
|
1360
1627
|
while (position < bytes.length) {
|
|
@@ -1363,7 +1630,8 @@ async function safeRead(root, path, maxBytes) {
|
|
|
1363
1630
|
position += result.bytesRead;
|
|
1364
1631
|
}
|
|
1365
1632
|
const after = await file.stat();
|
|
1366
|
-
if (position !== before.size || after.size !== before.size || after.mtimeMs !== stat2.mtimeMs)
|
|
1633
|
+
if (position !== before.size || after.size !== before.size || after.mtimeMs !== stat2.mtimeMs)
|
|
1634
|
+
throw new Error("Patch file changed while reading");
|
|
1367
1635
|
await assertParents(root, path);
|
|
1368
1636
|
return bytes.subarray(0, position);
|
|
1369
1637
|
} finally {
|
|
@@ -1381,7 +1649,7 @@ async function assertParents(root, path) {
|
|
|
1381
1649
|
const info = await lstat2(cursor);
|
|
1382
1650
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Patch parent is not a safe directory");
|
|
1383
1651
|
} catch (error) {
|
|
1384
|
-
if (
|
|
1652
|
+
if (hasErrorCode2(error, "ENOENT")) return;
|
|
1385
1653
|
throw error;
|
|
1386
1654
|
}
|
|
1387
1655
|
}
|
|
@@ -1397,7 +1665,7 @@ async function missingDirectories(root, path) {
|
|
|
1397
1665
|
const info = await lstat2(cursor);
|
|
1398
1666
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Patch parent is unsafe");
|
|
1399
1667
|
} catch (error) {
|
|
1400
|
-
if (!
|
|
1668
|
+
if (!hasErrorCode2(error, "ENOENT")) throw error;
|
|
1401
1669
|
missing.push(cursor);
|
|
1402
1670
|
}
|
|
1403
1671
|
}
|
|
@@ -1426,7 +1694,7 @@ async function privateDirectory(path) {
|
|
|
1426
1694
|
await safeRoot(parent);
|
|
1427
1695
|
break;
|
|
1428
1696
|
} catch (error) {
|
|
1429
|
-
if (!
|
|
1697
|
+
if (!hasErrorCode2(error, "ENOENT")) throw error;
|
|
1430
1698
|
missing.unshift(parent);
|
|
1431
1699
|
parent = dirname(parent);
|
|
1432
1700
|
}
|
|
@@ -1435,7 +1703,7 @@ async function privateDirectory(path) {
|
|
|
1435
1703
|
try {
|
|
1436
1704
|
await mkdir2(directory, { mode: 448 });
|
|
1437
1705
|
} catch (error) {
|
|
1438
|
-
if (!
|
|
1706
|
+
if (!hasErrorCode2(error, "EEXIST")) throw error;
|
|
1439
1707
|
}
|
|
1440
1708
|
await safeRoot(directory);
|
|
1441
1709
|
}
|
|
@@ -1458,7 +1726,8 @@ async function syncDirectory(path) {
|
|
|
1458
1726
|
directory = await open4(path, "r");
|
|
1459
1727
|
await directory.sync();
|
|
1460
1728
|
} catch (error) {
|
|
1461
|
-
if (process.platform !== "win32" || !["EPERM", "EACCES", "EINVAL", "EISDIR"].some((code) =>
|
|
1729
|
+
if (process.platform !== "win32" || !["EPERM", "EACCES", "EINVAL", "EISDIR"].some((code) => hasErrorCode2(error, code)))
|
|
1730
|
+
throw error;
|
|
1462
1731
|
} finally {
|
|
1463
1732
|
await directory?.close();
|
|
1464
1733
|
}
|
|
@@ -1472,7 +1741,10 @@ function checkedPath(root, path) {
|
|
|
1472
1741
|
}
|
|
1473
1742
|
__name(checkedPath, "checkedPath");
|
|
1474
1743
|
function assertRelativePath(path) {
|
|
1475
|
-
if (typeof path !== "string" || !path || isAbsolute2(path) || /[\\:\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u.test(path) || path.split("/").some(
|
|
1744
|
+
if (typeof path !== "string" || !path || isAbsolute2(path) || /[\\:\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u.test(path) || path.split("/").some(
|
|
1745
|
+
(part) => !part || part === "." || part === ".." || /[. ]$/u.test(part) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu.test(part)
|
|
1746
|
+
))
|
|
1747
|
+
throw new Error("Unsafe or nonportable patch path");
|
|
1476
1748
|
}
|
|
1477
1749
|
__name(assertRelativePath, "assertRelativePath");
|
|
1478
1750
|
function assertPatchId(id) {
|
|
@@ -1488,10 +1760,6 @@ function hash2(value) {
|
|
|
1488
1760
|
return createHash3("sha256").update(value).digest("hex");
|
|
1489
1761
|
}
|
|
1490
1762
|
__name(hash2, "hash");
|
|
1491
|
-
function hasCode(error, code) {
|
|
1492
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
1493
|
-
}
|
|
1494
|
-
__name(hasCode, "hasCode");
|
|
1495
1763
|
|
|
1496
1764
|
// apps/maybecode/src/team-v2.ts
|
|
1497
1765
|
var VERSION = "maybecode-team-v2";
|
|
@@ -1500,26 +1768,38 @@ var DEFAULT_RUN_BUDGET = { maxSteps: 10, maxModelCalls: 10, maxToolCalls: 24, ma
|
|
|
1500
1768
|
async function runTeamV2Command(command, dependencies) {
|
|
1501
1769
|
const id = command.action === "run" ? randomUUID2() : command.value;
|
|
1502
1770
|
if (!/^[A-Za-z0-9_-]{1,128}$/u.test(id)) throw new Error("Invalid team id");
|
|
1503
|
-
const directory = await canonicalPotentialPath(
|
|
1771
|
+
const directory = await canonicalPotentialPath(
|
|
1772
|
+
join4(resolve3(command.dataDirectory ?? getDefaultMaybeCodeDataDirectory()), "teams", id)
|
|
1773
|
+
);
|
|
1504
1774
|
const write = /* @__PURE__ */ __name((text2) => dependencies.write(safeTerminal(text2)), "write");
|
|
1505
1775
|
let config;
|
|
1506
1776
|
let manifest;
|
|
1507
1777
|
if (command.action === "run") {
|
|
1508
|
-
if (!command.value.trim() || Buffer.byteLength(command.value) > 32768)
|
|
1778
|
+
if (!command.value.trim() || Buffer.byteLength(command.value) > 32768)
|
|
1779
|
+
throw new Error("Team prompt must contain 1-32768 bytes");
|
|
1509
1780
|
const workspace = await realpath3(resolve3(command.workspace ?? process.cwd()));
|
|
1510
|
-
if (!(await stat(workspace)).isDirectory() || inside2(workspace, directory) || inside2(directory, workspace))
|
|
1781
|
+
if (!(await stat(workspace)).isDirectory() || inside2(workspace, directory) || inside2(directory, workspace))
|
|
1782
|
+
throw new Error("Team data directory must be outside and not overlap the source workspace");
|
|
1511
1783
|
const mode = command.mode ?? "read-only";
|
|
1512
|
-
const plan = command.planPath ? await loadTeamPlan(command.planPath, { mode }) : createTeamPreset(command.preset ?? "supervisor", command.value, {
|
|
1784
|
+
const plan = command.planPath ? await loadTeamPlan(command.planPath, { mode }) : createTeamPreset(command.preset ?? "supervisor", command.value, {
|
|
1785
|
+
mode,
|
|
1786
|
+
...command.model ? { model: command.model } : {}
|
|
1787
|
+
});
|
|
1513
1788
|
config = await (dependencies.loadConfig ?? loadMayConfig)(command.configPath ? { path: command.configPath } : {});
|
|
1514
1789
|
const fallback = command.model ?? (Object.values(plan.roles).every((role) => role.model !== void 0) ? Object.values(plan.roles)[0].model : void 0);
|
|
1515
1790
|
const defaultModel = selectMaybeCodeModel(config, fallback ? { model: fallback } : {});
|
|
1516
1791
|
const roleModels = {};
|
|
1517
1792
|
for (const [name, role] of Object.entries(plan.roles)) {
|
|
1518
1793
|
const selected = selectMaybeCodeModel(config, { model: role.model ?? defaultModel.profile });
|
|
1519
|
-
const runBudget = resolveRunBudget2(
|
|
1794
|
+
const runBudget = resolveRunBudget2(
|
|
1795
|
+
resolveRunBudget2(readMaybeCodeSettings(config).runBudget, DEFAULT_RUN_BUDGET),
|
|
1796
|
+
role.runBudget
|
|
1797
|
+
);
|
|
1520
1798
|
roleModels[name] = { profile: selected.profile, fingerprint: modelFingerprint(selected), runBudget };
|
|
1521
1799
|
}
|
|
1522
1800
|
manifest = {
|
|
1801
|
+
execution: readMaybeCodeSettings(config).execution ?? { mode: "host" },
|
|
1802
|
+
fetch: readMaybeCodeSettings(config).fetch ?? {},
|
|
1523
1803
|
format: 1,
|
|
1524
1804
|
version: VERSION,
|
|
1525
1805
|
id,
|
|
@@ -1540,7 +1820,8 @@ async function runTeamV2Command(command, dependencies) {
|
|
|
1540
1820
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
1541
1821
|
await writeJson(join4(directory, "manifest.json"), manifest, true);
|
|
1542
1822
|
} else manifest = await readManifest(directory, id);
|
|
1543
|
-
if (inside2(manifest.workspace, directory) || inside2(directory, manifest.workspace))
|
|
1823
|
+
if (inside2(manifest.workspace, directory) || inside2(directory, manifest.workspace))
|
|
1824
|
+
throw new Error("Team data and source directories overlap");
|
|
1544
1825
|
const authorityVersion = `${VERSION}-${teamReviewDigest({ mode: manifest.mode, allowChecks: manifest.allowChecks, plan: manifest.plan, roleModels: manifest.roleModels })}`;
|
|
1545
1826
|
const budgetLimits = { maxModelCalls: manifest.maxModelCalls, maxTotalTokens: manifest.maxTotalTokens };
|
|
1546
1827
|
if (command.action === "status") {
|
|
@@ -1551,10 +1832,12 @@ Records: ${directory}
|
|
|
1551
1832
|
write(await inspectTeam(directory, id, budgetLimits));
|
|
1552
1833
|
const path = join4(directory, "acceptance.json");
|
|
1553
1834
|
try {
|
|
1554
|
-
write(
|
|
1555
|
-
`)
|
|
1835
|
+
write(
|
|
1836
|
+
`Last acceptance projection (may be stale): ${await withFileAccess(path, "shared", () => readFile(path, "utf8"))}
|
|
1837
|
+
`
|
|
1838
|
+
);
|
|
1556
1839
|
} catch (error) {
|
|
1557
|
-
if (!
|
|
1840
|
+
if (!hasErrorCode3(error, "ENOENT")) throw error;
|
|
1558
1841
|
}
|
|
1559
1842
|
return 0;
|
|
1560
1843
|
}
|
|
@@ -1566,15 +1849,25 @@ Records: ${directory}
|
|
|
1566
1849
|
}
|
|
1567
1850
|
const executes = command.action === "run" || command.action === "resume";
|
|
1568
1851
|
const selections = /* @__PURE__ */ new Map();
|
|
1852
|
+
let recording;
|
|
1569
1853
|
if (executes) {
|
|
1570
1854
|
config ??= await (dependencies.loadConfig ?? loadMayConfig)({ path: manifest.configPath });
|
|
1855
|
+
recording = resolveMaybeCodeRecording(config);
|
|
1571
1856
|
for (const [name, binding] of Object.entries(manifest.roleModels)) {
|
|
1572
1857
|
const selected = selectMaybeCodeModel(config, { model: binding.profile });
|
|
1573
|
-
if (modelFingerprint(selected) !== binding.fingerprint)
|
|
1858
|
+
if (modelFingerprint(selected) !== binding.fingerprint)
|
|
1859
|
+
throw new Error(`Model configuration changed for role ${name}; restore it before resuming`);
|
|
1574
1860
|
selections.set(name, selected);
|
|
1575
1861
|
}
|
|
1576
1862
|
}
|
|
1577
1863
|
const ledger = await FileSharedBudget.open(join4(directory, "budget"), id, budgetLimits);
|
|
1864
|
+
const settings = readMaybeCodeSettings({
|
|
1865
|
+
providers: {},
|
|
1866
|
+
models: {},
|
|
1867
|
+
path: manifest.configPath,
|
|
1868
|
+
apps: { maybecode: { execution: manifest.execution ?? { mode: "host" }, fetch: manifest.fetch ?? {} } }
|
|
1869
|
+
});
|
|
1870
|
+
const executionEnvironments = new TeamExecutionEnvironments(settings.execution);
|
|
1578
1871
|
let workspaces;
|
|
1579
1872
|
let artifacts;
|
|
1580
1873
|
let verification;
|
|
@@ -1583,9 +1876,31 @@ Records: ${directory}
|
|
|
1583
1876
|
let timer;
|
|
1584
1877
|
let cancelling;
|
|
1585
1878
|
let retryAuthorized = false;
|
|
1879
|
+
const backgroundTasks = /* @__PURE__ */ new Set();
|
|
1880
|
+
const backgroundErrors = [];
|
|
1881
|
+
let failBackground;
|
|
1882
|
+
const backgroundFailed = new Promise((resolve4) => {
|
|
1883
|
+
failBackground = resolve4;
|
|
1884
|
+
});
|
|
1885
|
+
const trackBackground = /* @__PURE__ */ __name((operation) => {
|
|
1886
|
+
const task = operation.then(
|
|
1887
|
+
() => {
|
|
1888
|
+
backgroundTasks.delete(task);
|
|
1889
|
+
},
|
|
1890
|
+
(error) => {
|
|
1891
|
+
backgroundTasks.delete(task);
|
|
1892
|
+
backgroundErrors.push(error);
|
|
1893
|
+
failBackground(error);
|
|
1894
|
+
cancel();
|
|
1895
|
+
}
|
|
1896
|
+
);
|
|
1897
|
+
backgroundTasks.add(task);
|
|
1898
|
+
}, "trackBackground");
|
|
1586
1899
|
const cancel = /* @__PURE__ */ __name(() => {
|
|
1587
|
-
if (runtime
|
|
1588
|
-
|
|
1900
|
+
if (runtime && !cancelling) {
|
|
1901
|
+
cancelling = runtime.cancel(`cancel-${id}`);
|
|
1902
|
+
trackBackground(cancelling);
|
|
1903
|
+
}
|
|
1589
1904
|
}, "cancel");
|
|
1590
1905
|
try {
|
|
1591
1906
|
artifacts = await FileArtifactStore.open(join4(directory, "artifacts"), id, {
|
|
@@ -1595,8 +1910,15 @@ Records: ${directory}
|
|
|
1595
1910
|
maxArtifactBytes: 262144,
|
|
1596
1911
|
maxTotalBytes: 16777216
|
|
1597
1912
|
});
|
|
1598
|
-
workspaces = await TaskWorkspaceManager.open({
|
|
1599
|
-
|
|
1913
|
+
workspaces = await TaskWorkspaceManager.open({
|
|
1914
|
+
sourceDirectory: manifest.workspace,
|
|
1915
|
+
directory: join4(directory, "workspaces")
|
|
1916
|
+
});
|
|
1917
|
+
verification = await TeamVerificationStore.open({
|
|
1918
|
+
directory: join4(directory, "verification"),
|
|
1919
|
+
checks: manifest.plan.checks,
|
|
1920
|
+
executionFor: /* @__PURE__ */ __name((workspace) => executionEnvironments.get(workspace), "executionFor")
|
|
1921
|
+
});
|
|
1600
1922
|
const sessionStore = new FileSessionStore2(join4(directory, "sessions"));
|
|
1601
1923
|
const models = /* @__PURE__ */ new Map();
|
|
1602
1924
|
const modelFor = /* @__PURE__ */ __name((name) => {
|
|
@@ -1607,68 +1929,97 @@ Records: ${directory}
|
|
|
1607
1929
|
const maxOutputTokens = Math.min(
|
|
1608
1930
|
4096,
|
|
1609
1931
|
selected.limits?.maxOutputTokens ?? 4096,
|
|
1610
|
-
...[selected.options.maxTokens, selected.options.maxOutputTokens].filter(
|
|
1932
|
+
...[selected.options.maxTokens, selected.options.maxOutputTokens].filter(
|
|
1933
|
+
(n) => typeof n === "number" && n > 0 && Number.isSafeInteger(n)
|
|
1934
|
+
)
|
|
1611
1935
|
);
|
|
1612
1936
|
const selection = {
|
|
1613
1937
|
...selected,
|
|
1614
1938
|
options: { ...selected.options, maxTokens: maxOutputTokens, maxOutputTokens },
|
|
1615
1939
|
limits: { ...selected.limits, maxOutputTokens }
|
|
1616
1940
|
};
|
|
1617
|
-
model = ledger.wrapModel((dependencies.createModel ?? createMaybeCodeModel)(selection), {
|
|
1941
|
+
model = ledger.wrapModel((dependencies.createModel ?? createMaybeCodeModel)(selection), {
|
|
1942
|
+
reservation: { totalTokens: RESERVATION }
|
|
1943
|
+
});
|
|
1618
1944
|
models.set(name, model);
|
|
1619
1945
|
}
|
|
1620
1946
|
return model;
|
|
1621
1947
|
}, "modelFor");
|
|
1622
1948
|
const agents = {};
|
|
1623
1949
|
for (const [name, role] of Object.entries(manifest.plan.roles)) {
|
|
1624
|
-
const scoped = /* @__PURE__ */ __name((execution, workspace) =>
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1950
|
+
const scoped = /* @__PURE__ */ __name(async (execution, workspace) => {
|
|
1951
|
+
const environment = await executionEnvironments.get(workspace);
|
|
1952
|
+
return createApplicationAgent({
|
|
1953
|
+
version: authorityVersion,
|
|
1954
|
+
store: sessionStore,
|
|
1955
|
+
delegation: role.delegateTo.length > 0,
|
|
1956
|
+
messaging: role.messaging,
|
|
1957
|
+
definition: /* @__PURE__ */ __name(({ tools }) => {
|
|
1958
|
+
const available = [
|
|
1959
|
+
createReadTool({ cwd: workspace, maxBytes: 262144, maxLines: 160 }),
|
|
1960
|
+
createGlobTool({ cwd: workspace, execution: environment }),
|
|
1961
|
+
createGrepTool({ cwd: workspace, execution: environment }),
|
|
1962
|
+
createFetchTool(settings?.fetch ?? {}),
|
|
1963
|
+
createListTool(workspace),
|
|
1964
|
+
...artifacts.forTask(execution.task.id).tools(),
|
|
1965
|
+
...verification.forTask(execution, workspace, { allowCommands: manifest.allowChecks }).tools(),
|
|
1966
|
+
...manifest.mode === "coding" ? [
|
|
1967
|
+
createWriteTool({ cwd: workspace, maxBytes: 262144 }),
|
|
1968
|
+
createEditTool({ cwd: workspace, maxBytes: 262144 })
|
|
1969
|
+
] : []
|
|
1970
|
+
];
|
|
1971
|
+
const selectedTools = available.filter((tool) => role.tools.includes(tool.name)).map((tool) => {
|
|
1972
|
+
if (!["write", "edit"].includes(tool.name)) return tool;
|
|
1973
|
+
return {
|
|
1974
|
+
...tool,
|
|
1975
|
+
async execute(input, context) {
|
|
1976
|
+
if ((await verification.snapshot()).checks.some(
|
|
1977
|
+
(check) => check.status === "unknown" || check.taskId === execution.task.id && check.status === "pending"
|
|
1978
|
+
))
|
|
1979
|
+
throw new Error(
|
|
1980
|
+
"Pending or unknown check effects block editing; stop and reconcile uncertain processes first"
|
|
1981
|
+
);
|
|
1982
|
+
return tool.execute(input, context);
|
|
1983
|
+
}
|
|
1984
|
+
};
|
|
1985
|
+
});
|
|
1986
|
+
const allowed = new Set([...selectedTools, ...tools].map((tool) => tool.name));
|
|
1987
|
+
const roleBudget = manifest.roleModels[name].runBudget;
|
|
1988
|
+
const budget2 = config ? resolveRunBudget2(roleBudget, readMaybeCodeSettings(config).runBudget) : roleBudget;
|
|
1989
|
+
if (!recording) throw new Error("A host control command cannot start a model");
|
|
1990
|
+
return defineAgent({
|
|
1991
|
+
...recording,
|
|
1992
|
+
model: modelFor(name),
|
|
1993
|
+
tools: [...selectedTools, ...tools],
|
|
1994
|
+
runBudget: budget2,
|
|
1995
|
+
instructions: instructions(manifest, name, role),
|
|
1996
|
+
toolScope: { workspaceId: workspace },
|
|
1997
|
+
permissionPolicy: /* @__PURE__ */ __name((check) => allowed.has(check.tool.name) ? "allow" : "deny", "permissionPolicy")
|
|
1998
|
+
});
|
|
1999
|
+
}, "definition")
|
|
2000
|
+
});
|
|
2001
|
+
}, "scoped");
|
|
1657
2002
|
const publish = /* @__PURE__ */ __name(async (execution, output) => {
|
|
1658
|
-
await artifacts.forTask(execution.task.id).publish(
|
|
1659
|
-
`${execution.task.
|
|
1660
|
-
|
|
1661
|
-
|
|
2003
|
+
await artifacts.forTask(execution.task.id).publish(`${execution.task.dispatchId}-${execution.task.turn ?? 0}-final`, {
|
|
2004
|
+
name: `${execution.task.id.slice(0, 100)}-result.md`,
|
|
2005
|
+
text: output.text,
|
|
2006
|
+
mimeType: "text/markdown"
|
|
2007
|
+
});
|
|
1662
2008
|
}, "publish");
|
|
1663
2009
|
agents[name] = {
|
|
1664
2010
|
version: authorityVersion,
|
|
1665
2011
|
async execute(execution, context) {
|
|
1666
|
-
if ((await verification.snapshot()).checks.some((check) => check.status === "unknown"))
|
|
2012
|
+
if ((await verification.snapshot()).checks.some((check) => check.status === "unknown"))
|
|
2013
|
+
throw new Error("Unknown check effects block a new Agent Run; reconcile before retrying");
|
|
1667
2014
|
const isolated = await workspaces.prepare(execution.task.id);
|
|
1668
2015
|
const dependencyResults = [];
|
|
1669
2016
|
for (const dependency of execution.dependencies) {
|
|
1670
2017
|
const source = await workspaces.prepare(dependency.taskId);
|
|
1671
|
-
const acceptance2 = await verification.acceptance(
|
|
2018
|
+
const acceptance2 = await verification.acceptance(
|
|
2019
|
+
dependency.taskId,
|
|
2020
|
+
source.directory,
|
|
2021
|
+
executionOf(runtime.snapshot(), dependency.taskId)
|
|
2022
|
+
);
|
|
1672
2023
|
const record = {
|
|
1673
2024
|
taskId: dependency.taskId,
|
|
1674
2025
|
status: acceptance2.status,
|
|
@@ -1678,26 +2029,36 @@ Records: ${directory}
|
|
|
1678
2029
|
evidenceValid: acceptance2.report?.evidence.every((item) => item.valid) ?? false,
|
|
1679
2030
|
detail: acceptance2.detail
|
|
1680
2031
|
};
|
|
1681
|
-
dependencyResults.push({
|
|
1682
|
-
...dependency
|
|
1683
|
-
|
|
2032
|
+
dependencyResults.push({
|
|
2033
|
+
...dependency,
|
|
2034
|
+
output: {
|
|
2035
|
+
...dependency.output,
|
|
2036
|
+
text: `${dependency.output.text}
|
|
1684
2037
|
|
|
1685
2038
|
Host verification record (scoped to that task's private workspace, not the source checkout or every claim):
|
|
1686
2039
|
${JSON.stringify(record)}`
|
|
1687
|
-
|
|
2040
|
+
}
|
|
2041
|
+
});
|
|
1688
2042
|
}
|
|
1689
2043
|
const submitted = { ...execution, dependencies: dependencyResults };
|
|
1690
|
-
const output = await scoped(submitted, isolated.directory).execute(submitted, context);
|
|
2044
|
+
const output = await (await scoped(submitted, isolated.directory)).execute(submitted, context);
|
|
1691
2045
|
if (!("yielded" in output)) {
|
|
1692
2046
|
await publish(execution, output);
|
|
1693
|
-
for (const check of manifest.plan.checks.filter(
|
|
1694
|
-
|
|
2047
|
+
for (const check of manifest.plan.checks.filter(
|
|
2048
|
+
(check2) => check2.taskId === execution.task.id && check2.type !== "command"
|
|
2049
|
+
)) {
|
|
2050
|
+
await verification.runCheck(
|
|
2051
|
+
teamReviewDigest({ auto: execution.task.dispatchId, turn: execution.task.turn ?? 0, check: check.id }),
|
|
2052
|
+
check,
|
|
2053
|
+
isolated.directory,
|
|
2054
|
+
{ execution, signal: context.signal }
|
|
2055
|
+
);
|
|
1695
2056
|
}
|
|
1696
2057
|
}
|
|
1697
2058
|
return output;
|
|
1698
2059
|
},
|
|
1699
2060
|
async recover(execution) {
|
|
1700
|
-
const output = await scoped(execution, manifest.workspace).recover(execution);
|
|
2061
|
+
const output = await (await scoped(execution, manifest.workspace)).recover(execution);
|
|
1701
2062
|
if (output.status === "completed") await publish(execution, output.output);
|
|
1702
2063
|
return output;
|
|
1703
2064
|
}
|
|
@@ -1716,7 +2077,8 @@ ${JSON.stringify(record)}`
|
|
|
1716
2077
|
if (retryAuthorized) return true;
|
|
1717
2078
|
const state = runtime?.snapshot(), current = state?.tasks.find((entry) => entry.id === task.id);
|
|
1718
2079
|
const previous = current?.attempts?.at(-1);
|
|
1719
|
-
if (current?.status !== "queued" || !previous || previous.task.dispatchId !== task.dispatchId || previous.task.sessionId !== task.sessionId || previous.finding !== finding || !/^retry-[a-f0-9]{64}$/u.test(previous.commandId))
|
|
2080
|
+
if (current?.status !== "queued" || !previous || previous.task.dispatchId !== task.dispatchId || previous.task.sessionId !== task.sessionId || previous.finding !== finding || !/^retry-[a-f0-9]{64}$/u.test(previous.commandId))
|
|
2081
|
+
return false;
|
|
1720
2082
|
const receipt = state?.commands[previous.commandId];
|
|
1721
2083
|
if (!receipt) return false;
|
|
1722
2084
|
const value = JSON.parse(receipt);
|
|
@@ -1739,26 +2101,31 @@ ${JSON.stringify(record)}`
|
|
|
1739
2101
|
maxConcurrent: manifest.maxConcurrent
|
|
1740
2102
|
}
|
|
1741
2103
|
}) : await CoordinationRuntime.resume(options);
|
|
1742
|
-
write(
|
|
2104
|
+
write(
|
|
2105
|
+
`Team ${id}
|
|
1743
2106
|
Mode: ${manifest.mode}; source application requires a separate reviewed patch confirmation.
|
|
1744
2107
|
Records: ${directory}
|
|
1745
|
-
`
|
|
1746
|
-
|
|
2108
|
+
`
|
|
2109
|
+
);
|
|
2110
|
+
if (manifest.allowChecks)
|
|
2111
|
+
write("Configured test processes explicitly authorized; no OS sandbox or automatic dependency installation.\n");
|
|
1747
2112
|
relay = (async () => {
|
|
1748
2113
|
const statuses = /* @__PURE__ */ new Map();
|
|
1749
2114
|
for await (const event of runtime.events) {
|
|
1750
2115
|
if (event.type === "state.changed") {
|
|
1751
|
-
for (const task of event.snapshot.tasks)
|
|
1752
|
-
statuses.
|
|
1753
|
-
|
|
2116
|
+
for (const task of event.snapshot.tasks)
|
|
2117
|
+
if (statuses.get(task.id) !== task.status) {
|
|
2118
|
+
statuses.set(task.id, task.status);
|
|
2119
|
+
write(`[${task.id}] ${task.status}${task.detail ? `: ${task.detail}` : ""}
|
|
1754
2120
|
`);
|
|
1755
|
-
|
|
1756
|
-
} else if (event.type === "agent.event" && event.event.type === "run.event" && event.event.event.type === "tool.started")
|
|
2121
|
+
}
|
|
2122
|
+
} else if (event.type === "agent.event" && event.event.type === "run.event" && event.event.event.type === "tool.started")
|
|
2123
|
+
write(`[${event.taskId}] tool ${event.event.event.call.name}
|
|
1757
2124
|
`);
|
|
1758
2125
|
else if (event.type === "runtime.failed") write("Team runtime failed; inspect recovery records.\n");
|
|
1759
2126
|
}
|
|
1760
2127
|
})();
|
|
1761
|
-
|
|
2128
|
+
trackBackground(relay);
|
|
1762
2129
|
if (command.action === "retry" || command.action === "reconcile") {
|
|
1763
2130
|
const state = runtime.snapshot(), budget2 = await ledger.snapshot(), checks = await verification.snapshot();
|
|
1764
2131
|
const operation = command.action === "retry" ? retryImpact(state, command.task, command.finding) : await readTeamResolution(command.resolutionPath);
|
|
@@ -1768,58 +2135,85 @@ Records: ${directory}
|
|
|
1768
2135
|
Review digest: ${digest}
|
|
1769
2136
|
`);
|
|
1770
2137
|
if (!command.confirm) {
|
|
1771
|
-
write(
|
|
2138
|
+
write(
|
|
2139
|
+
"Preview only. Verify recorded effects/usage, then repeat with --confirm and this digest. No agent has executed.\n"
|
|
2140
|
+
);
|
|
1772
2141
|
return 0;
|
|
1773
2142
|
}
|
|
1774
2143
|
if (command.confirm !== digest) throw new Error("Review digest changed or mismatched; inspect a fresh preview");
|
|
1775
2144
|
if (command.action === "retry") {
|
|
1776
|
-
if ((await ledger.totals()).blocked || checks.checks.some((check) => ["unknown", "pending"].includes(check.status)))
|
|
2145
|
+
if ((await ledger.totals()).blocked || checks.checks.some((check) => ["unknown", "pending"].includes(check.status)))
|
|
2146
|
+
throw new Error("Reconcile unknown usage/check effects before retrying; no reset or replay is allowed");
|
|
1777
2147
|
retryAuthorized = true;
|
|
1778
2148
|
await runtime.retryTask(`retry-${digest}`, command.task, command.finding);
|
|
1779
2149
|
} else {
|
|
1780
2150
|
const resolution = operation;
|
|
1781
|
-
if (resolution.kind === "budget")
|
|
1782
|
-
|
|
1783
|
-
else
|
|
2151
|
+
if (resolution.kind === "budget")
|
|
2152
|
+
await ledger.reconcile(resolution.callId, resolution.usage, resolution.finding);
|
|
2153
|
+
else if (resolution.kind === "check")
|
|
2154
|
+
await verification.reconcile(resolution.commandId, { status: resolution.outcome }, resolution.finding);
|
|
2155
|
+
else
|
|
2156
|
+
await runtime.resolveRecovery(`resolve-${digest}`, resolution.taskId, resolution.finding, {
|
|
2157
|
+
status: resolution.outcome,
|
|
2158
|
+
detail: resolution.finding
|
|
2159
|
+
});
|
|
1784
2160
|
}
|
|
1785
2161
|
write("Recorded. No agents or checks executed. Use team resume explicitly when ready.\n");
|
|
1786
2162
|
return 0;
|
|
1787
2163
|
}
|
|
1788
2164
|
if (command.action === "diff" || command.action === "apply") {
|
|
1789
|
-
if (manifest.mode !== "coding")
|
|
2165
|
+
if (manifest.mode !== "coding")
|
|
2166
|
+
throw new Error("Patch controls require a team explicitly created with --mode coding");
|
|
1790
2167
|
const state = runtime.snapshot();
|
|
1791
|
-
if (state.tasks.some((task) => !["completed", "failed", "cancelled"].includes(task.status)))
|
|
2168
|
+
if (state.tasks.some((task) => !["completed", "failed", "cancelled"].includes(task.status)))
|
|
2169
|
+
throw new Error("Settle all active/unknown team work before reviewing or applying a patch");
|
|
1792
2170
|
if (command.action === "diff") {
|
|
1793
2171
|
const taskIds = command.tasks.split(",");
|
|
1794
|
-
if (!taskIds.length || new Set(taskIds).size !== taskIds.length || taskIds.some((id2) => state.tasks.find((task) => task.id === id2)?.status !== "completed"))
|
|
2172
|
+
if (!taskIds.length || new Set(taskIds).size !== taskIds.length || taskIds.some((id2) => state.tasks.find((task) => task.id === id2)?.status !== "completed"))
|
|
2173
|
+
throw new Error("Select distinct completed task ids for review");
|
|
1795
2174
|
const patch2 = await createTeamPatch({ directory, workspaces, taskIds });
|
|
1796
2175
|
write(renderTeamPatchDiff(patch2));
|
|
1797
|
-
write(
|
|
2176
|
+
write(
|
|
2177
|
+
`
|
|
1798
2178
|
Patch: ${patch2.id}
|
|
1799
2179
|
Review digest: ${patch2.digest}
|
|
1800
2180
|
Only use team apply after reviewing the diff and current acceptance.
|
|
1801
|
-
`
|
|
2181
|
+
`
|
|
2182
|
+
);
|
|
1802
2183
|
return 0;
|
|
1803
2184
|
}
|
|
1804
2185
|
const patch = await readTeamPatch(directory, command.patch);
|
|
1805
2186
|
const uncheckedTasks = patch.taskSnapshots.map((task) => task.taskId).filter((taskId) => !manifest.plan.checks.some((check) => check.taskId === taskId));
|
|
1806
|
-
if (uncheckedTasks.length)
|
|
1807
|
-
|
|
1808
|
-
|
|
2187
|
+
if (uncheckedTasks.length)
|
|
2188
|
+
write(
|
|
2189
|
+
`Unverified patch tasks (human review only): ${uncheckedTasks.join(", ")}. Other tasks' passing checks do not verify these edits.
|
|
2190
|
+
`
|
|
2191
|
+
);
|
|
2192
|
+
if ((await ledger.totals()).blocked || (await verification.snapshot()).checks.some((check) => ["pending", "unknown"].includes(check.status)))
|
|
2193
|
+
throw new Error("Reconcile unknown team effects before applying source changes");
|
|
1809
2194
|
for (const checkTaskId of new Set(manifest.plan.checks.map((check) => check.taskId))) {
|
|
1810
2195
|
const task = state.tasks.find((task2) => task2.id === checkTaskId);
|
|
1811
|
-
if (task?.status !== "completed")
|
|
2196
|
+
if (task?.status !== "completed")
|
|
2197
|
+
throw new Error("All configured acceptance tasks must complete before source application");
|
|
1812
2198
|
const workspace = await workspaces.prepare(checkTaskId);
|
|
1813
|
-
if ((await verification.acceptance(checkTaskId, workspace.directory, executionOf(state, checkTaskId))).status !== "passed")
|
|
2199
|
+
if ((await verification.acceptance(checkTaskId, workspace.directory, executionOf(state, checkTaskId))).status !== "passed")
|
|
2200
|
+
throw new Error(`Acceptance is not passed/current for ${checkTaskId}; use team verify and inspect reports`);
|
|
1814
2201
|
}
|
|
1815
|
-
if (!manifest.plan.checks.length)
|
|
1816
|
-
|
|
2202
|
+
if (!manifest.plan.checks.length)
|
|
2203
|
+
write("No configured acceptance checks: this is an explicit human-reviewed, unverified source application.\n");
|
|
2204
|
+
const result2 = await applyTeamPatch({
|
|
2205
|
+
directory,
|
|
2206
|
+
workspaces,
|
|
2207
|
+
patchId: patch.id,
|
|
2208
|
+
confirmDigest: command.confirm
|
|
2209
|
+
});
|
|
1817
2210
|
write(`${JSON.stringify(result2, null, 2)}
|
|
1818
2211
|
`);
|
|
1819
2212
|
return result2.status === "applied" ? 0 : 1;
|
|
1820
2213
|
}
|
|
1821
2214
|
if (command.action === "verify") {
|
|
1822
|
-
if ((await verification.snapshot()).checks.some((check) => ["unknown", "pending"].includes(check.status)))
|
|
2215
|
+
if ((await verification.snapshot()).checks.some((check) => ["unknown", "pending"].includes(check.status)))
|
|
2216
|
+
throw new Error("Reconcile unknown check effects before running new verification");
|
|
1823
2217
|
const controller = new AbortController();
|
|
1824
2218
|
const signal = AbortSignal.any([controller.signal, ...dependencies.signal ? [dependencies.signal] : []]);
|
|
1825
2219
|
const observeCancellation = /* @__PURE__ */ __name(async () => {
|
|
@@ -1828,7 +2222,7 @@ Only use team apply after reviewing the diff and current acceptance.
|
|
|
1828
2222
|
await withFileAccess(path, "shared", () => stat(path));
|
|
1829
2223
|
controller.abort("Team verification cancelled");
|
|
1830
2224
|
} catch (error) {
|
|
1831
|
-
if (!
|
|
2225
|
+
if (!hasErrorCode3(error, "ENOENT")) {
|
|
1832
2226
|
controller.abort("Cannot establish cancellation state");
|
|
1833
2227
|
throw error;
|
|
1834
2228
|
}
|
|
@@ -1836,7 +2230,7 @@ Only use team apply after reviewing the diff and current acceptance.
|
|
|
1836
2230
|
}, "observeCancellation");
|
|
1837
2231
|
await observeCancellation();
|
|
1838
2232
|
timer = setInterval(() => {
|
|
1839
|
-
|
|
2233
|
+
trackBackground(observeCancellation());
|
|
1840
2234
|
}, 300);
|
|
1841
2235
|
const state = runtime.snapshot();
|
|
1842
2236
|
for (const check of manifest.plan.checks) {
|
|
@@ -1850,12 +2244,11 @@ Only use team apply after reviewing the diff and current acceptance.
|
|
|
1850
2244
|
continue;
|
|
1851
2245
|
}
|
|
1852
2246
|
const workspace = await workspaces.prepare(check.taskId);
|
|
1853
|
-
const result2 = await verification.runCheck(
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
);
|
|
2247
|
+
const result2 = await verification.runCheck(`host-${randomUUID2()}`, check, workspace.directory, {
|
|
2248
|
+
allowCommands: manifest.allowChecks,
|
|
2249
|
+
signal,
|
|
2250
|
+
execution: executionOf(state, check.taskId)
|
|
2251
|
+
});
|
|
1859
2252
|
write(`Check ${check.id}: ${result2.status}${result2.detail ? ` (${result2.detail})` : ""}
|
|
1860
2253
|
`);
|
|
1861
2254
|
}
|
|
@@ -1870,7 +2263,7 @@ Only use team apply after reviewing the diff and current acceptance.
|
|
|
1870
2263
|
await withFileAccess(path, "shared", () => stat(path));
|
|
1871
2264
|
cancel();
|
|
1872
2265
|
} catch (error) {
|
|
1873
|
-
if (!
|
|
2266
|
+
if (!hasErrorCode3(error, "ENOENT")) {
|
|
1874
2267
|
cancel();
|
|
1875
2268
|
throw error;
|
|
1876
2269
|
}
|
|
@@ -1880,14 +2273,21 @@ Only use team apply after reviewing the diff and current acceptance.
|
|
|
1880
2273
|
await checkCancellation();
|
|
1881
2274
|
if (dependencies.signal?.aborted) cancel();
|
|
1882
2275
|
timer = setInterval(() => {
|
|
1883
|
-
|
|
2276
|
+
trackBackground(checkCancellation());
|
|
1884
2277
|
}, 300);
|
|
1885
2278
|
await cancelling;
|
|
1886
2279
|
if ((await ledger.totals()).blocked || (await verification.snapshot()).checks.some((check) => check.status === "unknown")) {
|
|
1887
|
-
write(
|
|
2280
|
+
write(
|
|
2281
|
+
"Unknown usage/check effects or exceeded budget block execution. No new Run started; inspect and reconcile explicitly.\n"
|
|
2282
|
+
);
|
|
1888
2283
|
return 1;
|
|
1889
2284
|
}
|
|
1890
|
-
const result = await
|
|
2285
|
+
const result = await Promise.race([
|
|
2286
|
+
runtime.wait(),
|
|
2287
|
+
backgroundFailed.then((error) => {
|
|
2288
|
+
throw error;
|
|
2289
|
+
})
|
|
2290
|
+
]);
|
|
1891
2291
|
await cancelling;
|
|
1892
2292
|
const final = result.tasks.find((task) => task.id === manifest.plan.resultTaskId);
|
|
1893
2293
|
if (final?.output?.text) write(`
|
|
@@ -1900,9 +2300,11 @@ Team ${complete ? "completed" : "stopped with unfinished or failed tasks"}.
|
|
|
1900
2300
|
for (const task of result.tasks) write(` ${task.id}: ${task.status}
|
|
1901
2301
|
`);
|
|
1902
2302
|
const acceptance = await summarizeAcceptance(directory, result, manifest, workspaces, verification);
|
|
1903
|
-
write(
|
|
2303
|
+
write(
|
|
2304
|
+
`Acceptance: ${acceptance.status}. Completion is not a correctness guarantee.
|
|
1904
2305
|
Shared budget: ${JSON.stringify(await ledger.totals())}
|
|
1905
|
-
`
|
|
2306
|
+
`
|
|
2307
|
+
);
|
|
1906
2308
|
for (const ref of await artifacts.snapshot()) write(`Artifact [${ref.ownerTaskId}]: ${JSON.stringify(ref)}
|
|
1907
2309
|
`);
|
|
1908
2310
|
if (!complete) write(`Inspect: team status ${id}; use explicit reconciliation/retry controls before resume.
|
|
@@ -1912,10 +2314,23 @@ Shared budget: ${JSON.stringify(await ledger.totals())}
|
|
|
1912
2314
|
if (timer) clearInterval(timer);
|
|
1913
2315
|
dependencies.signal?.removeEventListener("abort", cancel);
|
|
1914
2316
|
try {
|
|
1915
|
-
await runtime
|
|
1916
|
-
await
|
|
2317
|
+
const results = await Promise.allSettled([...runtime ? [runtime.close()] : [], ...relay ? [relay] : []]);
|
|
2318
|
+
while (backgroundTasks.size) await Promise.all(backgroundTasks);
|
|
2319
|
+
const failures = [
|
|
2320
|
+
.../* @__PURE__ */ new Set([
|
|
2321
|
+
...backgroundErrors,
|
|
2322
|
+
...results.flatMap((result) => result.status === "rejected" ? [result.reason] : [])
|
|
2323
|
+
])
|
|
2324
|
+
];
|
|
2325
|
+
if (failures.length) throw new AggregateError(failures, "Team background operations failed");
|
|
1917
2326
|
} finally {
|
|
1918
|
-
await Promise.all([
|
|
2327
|
+
await Promise.all([
|
|
2328
|
+
verification?.close(),
|
|
2329
|
+
workspaces?.close(),
|
|
2330
|
+
artifacts?.close(),
|
|
2331
|
+
ledger.close(),
|
|
2332
|
+
executionEnvironments.close()
|
|
2333
|
+
]);
|
|
1919
2334
|
}
|
|
1920
2335
|
}
|
|
1921
2336
|
}
|
|
@@ -1936,15 +2351,26 @@ function executionOf(state, taskId) {
|
|
|
1936
2351
|
__name(executionOf, "executionOf");
|
|
1937
2352
|
async function summarizeAcceptance(directory, state, manifest, workspaces, verification) {
|
|
1938
2353
|
const tasks = {};
|
|
1939
|
-
for (const task of state.tasks)
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
2354
|
+
for (const task of state.tasks)
|
|
2355
|
+
if (task.status === "completed") {
|
|
2356
|
+
const workspace = await workspaces.prepare(task.id);
|
|
2357
|
+
const { report: _report, ...acceptance } = await verification.acceptance(
|
|
2358
|
+
task.id,
|
|
2359
|
+
workspace.directory,
|
|
2360
|
+
executionOf(state, task.id)
|
|
2361
|
+
);
|
|
2362
|
+
tasks[task.id] = acceptance;
|
|
2363
|
+
}
|
|
1944
2364
|
const required = [...new Set(manifest.plan.checks.map((check) => check.taskId))];
|
|
1945
2365
|
const failed = Object.values(tasks).some((task) => task.status === "failed");
|
|
1946
2366
|
const status = failed ? "failed" : required.length && state.tasks.every((task) => task.status === "completed") && required.every((id) => tasks[id]?.status === "passed") ? "passed" : "unverified";
|
|
1947
|
-
const result = {
|
|
2367
|
+
const result = {
|
|
2368
|
+
format: 1,
|
|
2369
|
+
revision: state.revision,
|
|
2370
|
+
status,
|
|
2371
|
+
tasks,
|
|
2372
|
+
note: "Scoped configured checks only, not proof of every natural-language claim. Verify rechecks exact workspace fingerprints."
|
|
2373
|
+
};
|
|
1948
2374
|
await writeJson(join4(directory, "acceptance.json"), result);
|
|
1949
2375
|
return result;
|
|
1950
2376
|
}
|
|
@@ -1972,29 +2398,33 @@ async function readManifest(directory, id) {
|
|
|
1972
2398
|
}
|
|
1973
2399
|
__name(readManifest, "readManifest");
|
|
1974
2400
|
function validateManifest(value, id) {
|
|
1975
|
-
if (!value || value.format !== 1 || value.version !== VERSION || value.id !== id || !isAbsolute3(value.workspace) || !isAbsolute3(value.configPath) || typeof value.prompt !== "string" || typeof value.model !== "string" || !["coding", "read-only"].includes(value.mode) || typeof value.allowChecks !== "boolean")
|
|
2401
|
+
if (!value || value.format !== 1 || value.version !== VERSION || value.id !== id || !isAbsolute3(value.workspace) || !isAbsolute3(value.configPath) || typeof value.prompt !== "string" || typeof value.model !== "string" || !["coding", "read-only"].includes(value.mode) || typeof value.allowChecks !== "boolean")
|
|
2402
|
+
throw new Error("Invalid v2 team manifest");
|
|
1976
2403
|
parseTeamPlan(value.plan, { mode: value.mode });
|
|
1977
|
-
if (![value.maxModelCalls, value.maxTotalTokens, value.maxConcurrent].every((n) => Number.isSafeInteger(n) && n > 0) || value.maxModelCalls > 1e4 || value.maxConcurrent > 8 || value.maxTotalTokens < RESERVATION)
|
|
1978
|
-
|
|
2404
|
+
if (![value.maxModelCalls, value.maxTotalTokens, value.maxConcurrent].every((n) => Number.isSafeInteger(n) && n > 0) || value.maxModelCalls > 1e4 || value.maxConcurrent > 8 || value.maxTotalTokens < RESERVATION)
|
|
2405
|
+
throw new Error("Invalid team limits; token budget must hold at least 32768 tokens");
|
|
2406
|
+
if (!value.roleModels || Object.keys(value.roleModels).length !== Object.keys(value.plan.roles).length)
|
|
2407
|
+
throw new Error("Invalid role model bindings");
|
|
1979
2408
|
for (const name of Object.keys(value.plan.roles)) {
|
|
1980
2409
|
const model = value.roleModels[name];
|
|
1981
|
-
if (!model || typeof model.profile !== "string" || !/^[a-f0-9]{64}$/u.test(model.fingerprint))
|
|
2410
|
+
if (!model || typeof model.profile !== "string" || !/^[a-f0-9]{64}$/u.test(model.fingerprint))
|
|
2411
|
+
throw new Error("Invalid role model binding");
|
|
1982
2412
|
resolveRunBudget2(model.runBudget);
|
|
1983
2413
|
}
|
|
1984
2414
|
}
|
|
1985
2415
|
__name(validateManifest, "validateManifest");
|
|
1986
2416
|
async function writeJson(path, value, exclusive = false) {
|
|
2417
|
+
const contents = `${JSON.stringify(value, null, 2)}
|
|
2418
|
+
`;
|
|
1987
2419
|
await withFileAccess(path, "exclusive", async () => {
|
|
1988
|
-
|
|
1989
|
-
const file = await open5(
|
|
2420
|
+
if (!exclusive) return (0, import_write_file_atomic.default)(path, contents, { mode: 384 });
|
|
2421
|
+
const file = await open5(path, "wx", 384);
|
|
1990
2422
|
try {
|
|
1991
|
-
await file.writeFile(
|
|
1992
|
-
`);
|
|
2423
|
+
await file.writeFile(contents);
|
|
1993
2424
|
await file.sync();
|
|
1994
2425
|
} finally {
|
|
1995
2426
|
await file.close();
|
|
1996
2427
|
}
|
|
1997
|
-
if (!exclusive) await rename2(target, path);
|
|
1998
2428
|
});
|
|
1999
2429
|
}
|
|
2000
2430
|
__name(writeJson, "writeJson");
|
|
@@ -2007,7 +2437,7 @@ async function canonicalPotentialPath(path) {
|
|
|
2007
2437
|
try {
|
|
2008
2438
|
return await realpath3(path);
|
|
2009
2439
|
} catch (error) {
|
|
2010
|
-
if (!
|
|
2440
|
+
if (!hasErrorCode3(error, "ENOENT")) throw error;
|
|
2011
2441
|
const parent = dirname2(path);
|
|
2012
2442
|
if (parent === path) throw error;
|
|
2013
2443
|
return join4(await canonicalPotentialPath(parent), relative3(parent, path));
|
|
@@ -2018,11 +2448,7 @@ function safeTerminal(text2) {
|
|
|
2018
2448
|
return text2.replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/gu, "");
|
|
2019
2449
|
}
|
|
2020
2450
|
__name(safeTerminal, "safeTerminal");
|
|
2021
|
-
function isMissing2(error) {
|
|
2022
|
-
return !!error && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
2023
|
-
}
|
|
2024
|
-
__name(isMissing2, "isMissing");
|
|
2025
2451
|
export {
|
|
2026
2452
|
runTeamV2Command
|
|
2027
2453
|
};
|
|
2028
|
-
//# sourceMappingURL=team-v2-
|
|
2454
|
+
//# sourceMappingURL=team-v2-YYO2G7DJ.js.map
|