@gajae-code/coding-agent 0.11.7 → 0.11.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +73 -1
- package/dist/types/cli/args.d.ts +0 -2
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/config/keybindings.d.ts +33 -3
- package/dist/types/config/model-profile-activation.d.ts +4 -3
- package/dist/types/config/model-profile-contract.d.ts +38 -0
- package/dist/types/config/model-profiles.d.ts +2 -3
- package/dist/types/config/settings-schema.d.ts +15 -2
- package/dist/types/edit/modes/patch.d.ts +13 -0
- package/dist/types/edit/path-mutation-lock.d.ts +15 -0
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/eval/py/tool-bridge.d.ts +1 -2
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +12 -5
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +8 -0
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +41 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +32 -17
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +17 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +135 -4
- package/dist/types/gjc-runtime/repository-binding.d.ts +63 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +44 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +1 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +24 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +5 -3
- package/dist/types/main.d.ts +8 -0
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/tool-status-header.d.ts +3 -2
- package/dist/types/modes/components/welcome.d.ts +4 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +5 -2
- package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
- package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
- package/dist/types/runtime-mcp/manager.d.ts +1 -1
- package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
- package/dist/types/runtime-mcp/redaction.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +7 -1
- package/dist/types/sdk/bus/config-commands.d.ts +2 -2
- package/dist/types/sdk/bus/config.d.ts +2 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +25 -0
- package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +105 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +15 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +128 -1
- package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +1 -1
- package/dist/types/sdk/host/query/handlers.d.ts +9 -0
- package/dist/types/sdk/index.d.ts +2 -0
- package/dist/types/sdk/prompt-status.d.ts +88 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/sdk/startup-capability.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +22 -3
- package/dist/types/session/internal/managed-session-storage.d.ts +24 -4
- package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
- package/dist/types/session/session-manager.d.ts +5 -1
- package/dist/types/session/session-storage.d.ts +31 -0
- package/dist/types/skill-state/active-state.d.ts +4 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +94 -0
- package/dist/types/tools/ask.d.ts +454 -174
- package/dist/types/tools/image-gen.d.ts +47 -1
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/utils/prompt-suggestion.d.ts +33 -0
- package/package.json +8 -8
- package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
- package/scripts/dogfood-repository-binding.ts +209 -0
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/run-test-manifest.ts +4 -4
- package/src/cli/args.ts +2 -92
- package/src/cli/mcp-cli.ts +4 -21
- package/src/cli/notify-cli.ts +5 -38
- package/src/cli.ts +50 -0
- package/src/commands/deep-interview.ts +9 -4
- package/src/commands/sdk.ts +2 -2
- package/src/commit/agentic/index.ts +3 -3
- package/src/commit/map-reduce/index.ts +2 -2
- package/src/config/keybindings.ts +157 -24
- package/src/config/model-profile-activation.ts +7 -18
- package/src/config/model-profile-contract.ts +161 -0
- package/src/config/model-profiles.ts +2 -3
- package/src/config/model-registry.ts +11 -1
- package/src/config/settings-schema.ts +17 -2
- package/src/coordinator-mcp/model-preset.ts +21 -74
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +80 -165
- package/src/defaults/gjc/skills/ralplan/SKILL.md +3 -1
- package/src/edit/index.ts +3 -6
- package/src/edit/modes/patch.ts +38 -1
- package/src/edit/modes/replace.ts +42 -0
- package/src/edit/path-mutation-lock.ts +112 -0
- package/src/eval/py/executor.ts +30 -11
- package/src/eval/py/prelude.py +2 -2
- package/src/eval/py/tool-bridge.ts +34 -18
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +2 -1
- package/src/gjc-runtime/deep-interview-ambiguity.ts +68 -47
- package/src/gjc-runtime/deep-interview-draft.ts +842 -0
- package/src/gjc-runtime/deep-interview-payload.ts +262 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +294 -226
- package/src/gjc-runtime/deep-interview-repair.ts +1674 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +264 -103
- package/src/gjc-runtime/deep-interview-state.ts +916 -4
- package/src/gjc-runtime/launch-tmux.ts +1 -1
- package/src/gjc-runtime/managed-owner-supervisor.ts +83 -4
- package/src/gjc-runtime/ralplan-runtime.ts +78 -4
- package/src/gjc-runtime/repository-binding.ts +267 -0
- package/src/gjc-runtime/session-state-sidecar.ts +25 -9
- package/src/gjc-runtime/state-writer.ts +166 -2
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -1
- package/src/gjc-runtime/tmux-sessions.ts +45 -6
- package/src/gjc-runtime/ultragoal-guard.ts +5 -5
- package/src/gjc-runtime/ultragoal-runtime.ts +181 -19
- package/src/gjc-runtime/workflow-manifest.generated.json +386 -1
- package/src/gjc-runtime/workflow-manifest.ts +191 -5
- package/src/internal-urls/docs-index.generated.ts +8 -6
- package/src/internal-urls/local-protocol.ts +33 -16
- package/src/internal-urls/mcp-protocol.ts +73 -11
- package/src/main.ts +25 -10
- package/src/modes/DESIGN.md +5 -0
- package/src/modes/acp/acp-agent.ts +50 -16
- package/src/modes/components/model-selector.ts +42 -8
- package/src/modes/components/plan-preview-overlay.ts +1 -1
- package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
- package/src/modes/components/tool-status-header.ts +20 -4
- package/src/modes/components/welcome.ts +32 -20
- package/src/modes/controllers/command-controller.ts +97 -54
- package/src/modes/controllers/event-controller.ts +10 -0
- package/src/modes/controllers/input-controller.ts +7 -2
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
- package/src/modes/controllers/selector-controller.ts +46 -54
- package/src/modes/interactive-mode.ts +70 -36
- package/src/modes/prompt-action-autocomplete.ts +32 -13
- package/src/modes/prompt-suggestion-controller.ts +96 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/hotkeys-markdown.ts +64 -36
- package/src/modes/utils/ui-helpers.ts +5 -3
- package/src/prompts/agents/architect.md +0 -1
- package/src/prompts/agents/critic.md +0 -1
- package/src/prompts/system/prompt-suggestion-system.md +32 -0
- package/src/prompts/system/system-prompt.md +7 -4
- package/src/prompts/tools/cron.md +3 -1
- package/src/runtime-mcp/json-rpc.ts +7 -4
- package/src/runtime-mcp/manager.ts +51 -53
- package/src/runtime-mcp/oauth-flow.ts +0 -72
- package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
- package/src/runtime-mcp/redaction.ts +30 -0
- package/src/runtime-mcp/transports/http.ts +13 -6
- package/src/sdk/broker/broker.ts +34 -20
- package/src/sdk/broker/lifecycle.ts +185 -36
- package/src/sdk/broker/session-index.ts +11 -1
- package/src/sdk/bus/chat-daemon-control.ts +20 -5
- package/src/sdk/bus/config-commands.ts +2 -2
- package/src/sdk/bus/config.ts +9 -1
- package/src/sdk/bus/conversation-store.ts +40 -6
- package/src/sdk/bus/index.ts +240 -65
- package/src/sdk/bus/lifecycle-commands.ts +47 -3
- package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
- package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
- package/src/sdk/bus/notification-orchestration.ts +12 -0
- package/src/sdk/bus/notification-service.ts +27 -2
- package/src/sdk/bus/prompt-reconciliation.ts +239 -0
- package/src/sdk/bus/recent-activity.ts +90 -26
- package/src/sdk/bus/telegram-daemon-contract.ts +15 -3
- package/src/sdk/bus/telegram-daemon-control.ts +145 -33
- package/src/sdk/bus/telegram-daemon.ts +853 -112
- package/src/sdk/bus/telegram-reference.ts +20 -2
- package/src/sdk/cli/session-cli.ts +15 -2
- package/src/sdk/host/control/dispatch.ts +1 -1
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +2 -6
- package/src/sdk/host/query/handlers.ts +70 -5
- package/src/sdk/index.ts +12 -0
- package/src/sdk/prompt-status.ts +97 -0
- package/src/sdk/protocol/operation-inventory.generated.json +47 -0
- package/src/sdk/protocol/operation-registry.ts +18 -4
- package/src/sdk/session-directory.ts +5 -1
- package/src/sdk/session.ts +2 -0
- package/src/sdk/startup-capability.ts +9 -1
- package/src/session/agent-session.ts +138 -5
- package/src/session/blob-store.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +998 -133
- package/src/session/internal/managed-session-storage.ts +268 -203
- package/src/session/internal/native-publish-outcome.ts +266 -0
- package/src/session/session-manager.ts +406 -360
- package/src/session/session-storage.ts +151 -11
- package/src/skill-state/active-state.ts +10 -0
- package/src/task/executor.ts +13 -5
- package/src/task/gjc-command.ts +2 -2
- package/src/task/index.ts +121 -15
- package/src/task/receipt.ts +3 -0
- package/src/task/types.ts +34 -3
- package/src/tools/ask.ts +199 -88
- package/src/tools/ast-edit.ts +2 -2
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser/screenshot-format.ts +6 -1
- package/src/tools/cron.ts +1 -1
- package/src/tools/image-gen.ts +178 -6
- package/src/tools/index.ts +4 -0
- package/src/tools/render-utils.ts +7 -1
- package/src/utils/edit-mode.ts +2 -2
- package/src/utils/prompt-suggestion.ts +265 -0
- package/src/utils/sixel.ts +3 -3
- package/src/web/insane/url-guard.ts +3 -1
- package/src/web/scrapers/docs-rs.ts +30 -5
|
@@ -167,6 +167,8 @@ export interface SessionStorage {
|
|
|
167
167
|
readSnapshotSync?(path: string): SessionStorageSnapshot;
|
|
168
168
|
statSync(path: string): SessionStorageStat;
|
|
169
169
|
listFilesSync(dir: string, pattern: string): string[];
|
|
170
|
+
/** List matching files with mtimes without issuing one JavaScript stat call per path. */
|
|
171
|
+
listFilesByMtime?(dir: string, pattern: string): Promise<Array<{ path: string; mtimeMs: number }>>;
|
|
170
172
|
/**
|
|
171
173
|
* Strict directory scan that never suppresses scan/root errors. Used by strict
|
|
172
174
|
* authorization inventory; the forgiving {@link listFilesSync} stays display-only.
|
|
@@ -259,6 +261,19 @@ export interface VerifiedSessionDeleteTarget {
|
|
|
259
261
|
detachedArtifactsPath?: string;
|
|
260
262
|
/** Identity-bound quarantine path retained when transcript unlink deferred cleanup. */
|
|
261
263
|
detachedTranscriptPath?: string;
|
|
264
|
+
/** Native-retained publisher successor observed during transcript cleanup. */
|
|
265
|
+
retainedTranscriptSuccessorPath?: string;
|
|
266
|
+
/** Native-retained exchange placeholder observed during transcript cleanup. */
|
|
267
|
+
retainedTranscriptPlaceholderPath?: string;
|
|
268
|
+
/** Native-retained transcript entry whose identity could not be verified. */
|
|
269
|
+
retainedTranscriptUnknownPath?: string;
|
|
270
|
+
/** Native-retained publisher successor observed during cleanup. */
|
|
271
|
+
retainedArtifactsSuccessorPath?: string;
|
|
272
|
+
/** Native-retained exchange placeholder observed during cleanup. */
|
|
273
|
+
retainedArtifactsPlaceholderPath?: string;
|
|
274
|
+
/** Native-retained entry whose identity could not be verified. */
|
|
275
|
+
retainedArtifactsUnknownPath?: string;
|
|
276
|
+
|
|
262
277
|
/** Caller-published, no-replace quarantine pathname for the next artifact detach. */
|
|
263
278
|
plannedArtifactsPath?: string;
|
|
264
279
|
/** Caller-published, no-replace quarantine pathname for the next transcript detach. */
|
|
@@ -288,6 +303,9 @@ export type VerifiedSessionDeleteResult =
|
|
|
288
303
|
artifactsTree: NativeDirectoryTreeSnapshot;
|
|
289
304
|
/** Transcript identity (unchanged) for retry binding. */
|
|
290
305
|
transcriptIdentity: SessionStorageFileIdentity;
|
|
306
|
+
retainedSuccessorPath?: string;
|
|
307
|
+
retainedPlaceholderPath?: string;
|
|
308
|
+
retainedUnknownPath?: string;
|
|
291
309
|
}
|
|
292
310
|
| {
|
|
293
311
|
kind: "cleanup_pending";
|
|
@@ -297,6 +315,9 @@ export type VerifiedSessionDeleteResult =
|
|
|
297
315
|
transcriptIdentity: SessionStorageFileIdentity;
|
|
298
316
|
/** Optional identity-bound transcript quarantine path for restart cleanup. */
|
|
299
317
|
detachedTranscriptPath?: string;
|
|
318
|
+
retainedSuccessorPath?: string;
|
|
319
|
+
retainedPlaceholderPath?: string;
|
|
320
|
+
retainedUnknownPath?: string;
|
|
300
321
|
};
|
|
301
322
|
|
|
302
323
|
/** Default OS-close dispatcher: a direct `fs.closeSync`. */
|
|
@@ -306,7 +327,22 @@ const defaultCloseAdapter: SessionStorageWriterCloseAdapter = {
|
|
|
306
327
|
},
|
|
307
328
|
};
|
|
308
329
|
|
|
309
|
-
type NativeExactUnlinkResult =
|
|
330
|
+
type NativeExactUnlinkResult =
|
|
331
|
+
| {
|
|
332
|
+
ok: true;
|
|
333
|
+
detachedPath?: string;
|
|
334
|
+
retainedSuccessorPath?: string;
|
|
335
|
+
retainedPlaceholderPath?: string;
|
|
336
|
+
retainedUnknownPath?: string;
|
|
337
|
+
}
|
|
338
|
+
| {
|
|
339
|
+
ok: false;
|
|
340
|
+
code: string;
|
|
341
|
+
detachedPath?: string;
|
|
342
|
+
retainedSuccessorPath?: string;
|
|
343
|
+
retainedPlaceholderPath?: string;
|
|
344
|
+
retainedUnknownPath?: string;
|
|
345
|
+
};
|
|
310
346
|
type NativeExactUnlink = (
|
|
311
347
|
path: string,
|
|
312
348
|
identity: {
|
|
@@ -671,6 +707,21 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
671
707
|
return [];
|
|
672
708
|
}
|
|
673
709
|
}
|
|
710
|
+
async listFilesByMtime(dir: string, pattern: string): Promise<Array<{ path: string; mtimeMs: number }>> {
|
|
711
|
+
const result = await native.glob({
|
|
712
|
+
path: dir,
|
|
713
|
+
pattern,
|
|
714
|
+
fileType: native.FileType.File,
|
|
715
|
+
recursive: false,
|
|
716
|
+
hidden: false,
|
|
717
|
+
gitignore: false,
|
|
718
|
+
sortByMtime: true,
|
|
719
|
+
});
|
|
720
|
+
return result.matches.map(match => ({
|
|
721
|
+
path: path.join(dir, match.path),
|
|
722
|
+
mtimeMs: match.mtime ?? 0,
|
|
723
|
+
}));
|
|
724
|
+
}
|
|
674
725
|
|
|
675
726
|
listFilesStrictSync(dir: string, pattern: string): string[] {
|
|
676
727
|
// Strict: never suppress scan/root errors. Authorization inventory depends on
|
|
@@ -766,7 +817,13 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
766
817
|
expectedArtifactsIdentity,
|
|
767
818
|
expectedArtifactsTree,
|
|
768
819
|
detachedArtifactsPath,
|
|
820
|
+
retainedArtifactsSuccessorPath,
|
|
821
|
+
retainedArtifactsPlaceholderPath,
|
|
822
|
+
retainedArtifactsUnknownPath,
|
|
769
823
|
detachedTranscriptPath,
|
|
824
|
+
retainedTranscriptSuccessorPath,
|
|
825
|
+
retainedTranscriptPlaceholderPath,
|
|
826
|
+
retainedTranscriptUnknownPath,
|
|
770
827
|
plannedArtifactsPath,
|
|
771
828
|
plannedTranscriptPath,
|
|
772
829
|
artifactsRemoved,
|
|
@@ -847,7 +904,8 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
847
904
|
if (
|
|
848
905
|
!expectedArtifactsIdentity ||
|
|
849
906
|
path.dirname(detachedArtifactsPath) !== path.dirname(transcriptPath) ||
|
|
850
|
-
!path.basename(detachedArtifactsPath).startsWith(".gjc-delete-")
|
|
907
|
+
(!path.basename(detachedArtifactsPath).startsWith(".gjc-delete-") &&
|
|
908
|
+
!detachedArtifactsPath.endsWith(".removing"))
|
|
851
909
|
) {
|
|
852
910
|
throw new SessionDeleteVerificationError("artifacts", "Detached artifact cleanup evidence is invalid");
|
|
853
911
|
}
|
|
@@ -882,17 +940,20 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
882
940
|
artifactsIdentity: expectedArtifactsIdentity,
|
|
883
941
|
detachedArtifactsPath: retainedRoot,
|
|
884
942
|
artifactsTree: expectedArtifactsTree,
|
|
943
|
+
...((removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath)
|
|
944
|
+
? { retainedSuccessorPath: removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath }
|
|
945
|
+
: {}),
|
|
946
|
+
...((removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath)
|
|
947
|
+
? { retainedPlaceholderPath: removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath }
|
|
948
|
+
: {}),
|
|
949
|
+
...((removal.retainedUnknownPath ?? retainedArtifactsUnknownPath)
|
|
950
|
+
? { retainedUnknownPath: removal.retainedUnknownPath ?? retainedArtifactsUnknownPath }
|
|
951
|
+
: {}),
|
|
885
952
|
transcriptIdentity,
|
|
886
953
|
};
|
|
887
954
|
}
|
|
888
955
|
}
|
|
889
956
|
|
|
890
|
-
if (artifactsRemoved && (detachedArtifactsPath || expectedArtifactsIdentity)) {
|
|
891
|
-
throw new SessionDeleteVerificationError(
|
|
892
|
-
"artifacts",
|
|
893
|
-
"Artifact phase receipt conflicts with pending artifact cleanup",
|
|
894
|
-
);
|
|
895
|
-
}
|
|
896
957
|
const artifactsDir = transcriptPath.slice(0, -6);
|
|
897
958
|
const artifactsIdentity = this.#optionalDirectoryIdentity(artifactsDir);
|
|
898
959
|
if (artifactsRemoved && artifactsIdentity) {
|
|
@@ -947,12 +1008,48 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
947
1008
|
directory: true,
|
|
948
1009
|
quarantineName: path.basename(plannedArtifactsPath),
|
|
949
1010
|
});
|
|
950
|
-
if (!detach.
|
|
1011
|
+
if (!detach.detachedPath) {
|
|
951
1012
|
throw new SessionDeleteVerificationError(
|
|
952
1013
|
"artifacts",
|
|
953
1014
|
`Exact artifact detach rejected: ${detach.ok ? "missing_path" : detach.code}`,
|
|
954
1015
|
);
|
|
955
1016
|
}
|
|
1017
|
+
if (!detach.ok && process.platform !== "win32") {
|
|
1018
|
+
let descriptor: number | undefined;
|
|
1019
|
+
try {
|
|
1020
|
+
descriptor = fs.openSync(
|
|
1021
|
+
path.dirname(transcriptPath),
|
|
1022
|
+
fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW,
|
|
1023
|
+
);
|
|
1024
|
+
const durableParent = fs.fstatSync(descriptor, { bigint: true });
|
|
1025
|
+
if (
|
|
1026
|
+
!durableParent.isDirectory() ||
|
|
1027
|
+
durableParent.dev !== parentIdentity.dev ||
|
|
1028
|
+
durableParent.ino !== parentIdentity.ino
|
|
1029
|
+
)
|
|
1030
|
+
throw new Error("parent_changed");
|
|
1031
|
+
fs.fsyncSync(descriptor);
|
|
1032
|
+
} catch (error) {
|
|
1033
|
+
throw new SessionDeleteVerificationError("artifacts", "durability_failed", { cause: toError(error) });
|
|
1034
|
+
} finally {
|
|
1035
|
+
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
if (!detach.ok) {
|
|
1039
|
+
return {
|
|
1040
|
+
kind: "cleanup_pending",
|
|
1041
|
+
phase: "artifacts",
|
|
1042
|
+
error: new SessionDeleteVerificationError("artifacts", `Exact artifact detach retained: ${detach.code}`),
|
|
1043
|
+
artifactsIdentity,
|
|
1044
|
+
detachedArtifactsPath: detach.detachedPath,
|
|
1045
|
+
artifactsTree,
|
|
1046
|
+
...(detach.retainedSuccessorPath ? { retainedSuccessorPath: detach.retainedSuccessorPath } : {}),
|
|
1047
|
+
...(detach.retainedPlaceholderPath ? { retainedPlaceholderPath: detach.retainedPlaceholderPath } : {}),
|
|
1048
|
+
...(detach.retainedUnknownPath ? { retainedUnknownPath: detach.retainedUnknownPath } : {}),
|
|
1049
|
+
|
|
1050
|
+
transcriptIdentity,
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
956
1053
|
const removal = removeDirectoryTreeExact(detach.detachedPath, artifactsTree);
|
|
957
1054
|
if (!removal.ok) {
|
|
958
1055
|
const retainedRoot = removal.detachedPath ?? detach.detachedPath;
|
|
@@ -971,6 +1068,15 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
971
1068
|
artifactsIdentity,
|
|
972
1069
|
detachedArtifactsPath: retainedRoot,
|
|
973
1070
|
artifactsTree,
|
|
1071
|
+
...((removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath)
|
|
1072
|
+
? { retainedSuccessorPath: removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath }
|
|
1073
|
+
: {}),
|
|
1074
|
+
...((removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath)
|
|
1075
|
+
? { retainedPlaceholderPath: removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath }
|
|
1076
|
+
: {}),
|
|
1077
|
+
...((removal.retainedUnknownPath ?? retainedArtifactsUnknownPath)
|
|
1078
|
+
? { retainedUnknownPath: removal.retainedUnknownPath ?? retainedArtifactsUnknownPath }
|
|
1079
|
+
: {}),
|
|
974
1080
|
transcriptIdentity,
|
|
975
1081
|
};
|
|
976
1082
|
}
|
|
@@ -1010,13 +1116,27 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
1010
1116
|
});
|
|
1011
1117
|
if (!deletion.ok) {
|
|
1012
1118
|
const error = exactUnlinkFailure(deletion);
|
|
1013
|
-
|
|
1119
|
+
const retainedAuthority =
|
|
1120
|
+
deletion.detachedPath ||
|
|
1121
|
+
deletion.retainedSuccessorPath ||
|
|
1122
|
+
deletion.retainedPlaceholderPath ||
|
|
1123
|
+
deletion.retainedUnknownPath;
|
|
1124
|
+
if ((error.kind === "identity" || error.kind === "symlink") && !retainedAuthority) throw error;
|
|
1014
1125
|
return {
|
|
1015
1126
|
kind: "cleanup_pending",
|
|
1016
1127
|
phase: "transcript",
|
|
1017
1128
|
error,
|
|
1018
1129
|
transcriptIdentity,
|
|
1019
1130
|
detachedTranscriptPath: deletion.detachedPath ?? detachedTranscriptPath,
|
|
1131
|
+
...((deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath)
|
|
1132
|
+
? { retainedSuccessorPath: deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath }
|
|
1133
|
+
: {}),
|
|
1134
|
+
...((deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath)
|
|
1135
|
+
? { retainedPlaceholderPath: deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath }
|
|
1136
|
+
: {}),
|
|
1137
|
+
...((deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath)
|
|
1138
|
+
? { retainedUnknownPath: deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath }
|
|
1139
|
+
: {}),
|
|
1020
1140
|
};
|
|
1021
1141
|
}
|
|
1022
1142
|
return { kind: "deleted" };
|
|
@@ -1058,13 +1178,27 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
1058
1178
|
});
|
|
1059
1179
|
if (!deletion.ok) {
|
|
1060
1180
|
const error = exactUnlinkFailure(deletion);
|
|
1061
|
-
|
|
1181
|
+
const retainedAuthority =
|
|
1182
|
+
deletion.detachedPath ||
|
|
1183
|
+
deletion.retainedSuccessorPath ||
|
|
1184
|
+
deletion.retainedPlaceholderPath ||
|
|
1185
|
+
deletion.retainedUnknownPath;
|
|
1186
|
+
if ((error.kind === "identity" || error.kind === "symlink") && !retainedAuthority) throw error;
|
|
1062
1187
|
return {
|
|
1063
1188
|
kind: "cleanup_pending",
|
|
1064
1189
|
phase: "transcript",
|
|
1065
1190
|
error,
|
|
1066
1191
|
transcriptIdentity,
|
|
1067
1192
|
detachedTranscriptPath: deletion.detachedPath,
|
|
1193
|
+
...((deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath)
|
|
1194
|
+
? { retainedSuccessorPath: deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath }
|
|
1195
|
+
: {}),
|
|
1196
|
+
...((deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath)
|
|
1197
|
+
? { retainedPlaceholderPath: deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath }
|
|
1198
|
+
: {}),
|
|
1199
|
+
...((deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath)
|
|
1200
|
+
? { retainedUnknownPath: deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath }
|
|
1201
|
+
: {}),
|
|
1068
1202
|
};
|
|
1069
1203
|
}
|
|
1070
1204
|
return { kind: "deleted" };
|
|
@@ -1353,6 +1487,12 @@ export class MemorySessionStorage implements SessionStorage {
|
|
|
1353
1487
|
}
|
|
1354
1488
|
return files;
|
|
1355
1489
|
}
|
|
1490
|
+
listFilesByMtime(dir: string, pattern: string): Promise<Array<{ path: string; mtimeMs: number }>> {
|
|
1491
|
+
const files = this.listFilesSync(dir, pattern)
|
|
1492
|
+
.map(path => ({ path, mtimeMs: this.statSync(path).mtimeMs }))
|
|
1493
|
+
.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
1494
|
+
return Promise.resolve(files);
|
|
1495
|
+
}
|
|
1356
1496
|
listFilesStrictSync(dir: string, pattern: string): string[] {
|
|
1357
1497
|
// In-memory scan never suppresses; identical to the display scan.
|
|
1358
1498
|
return this.listFilesSync(dir, pattern);
|
|
@@ -71,6 +71,8 @@ export interface SkillActiveEntry {
|
|
|
71
71
|
handoff_at?: string;
|
|
72
72
|
active_subskills?: ActiveSubskillEntry[];
|
|
73
73
|
source_state_revision?: number;
|
|
74
|
+
/** Durable ordering token from the source workflow mode-state, not this cache file's revision. */
|
|
75
|
+
committed_mode_state_revision?: number;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
export interface SkillActiveState {
|
|
@@ -114,6 +116,8 @@ export interface SyncSkillActiveStateOptions {
|
|
|
114
116
|
handoff_at?: string;
|
|
115
117
|
active_subskills?: ActiveSubskillEntry[];
|
|
116
118
|
sourceRevision?: number;
|
|
119
|
+
/** Durable ordering token from the source workflow mode-state, not this cache file's revision. */
|
|
120
|
+
committedModeRevision?: number;
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
const HUD_TEXT_LIMIT = 80;
|
|
@@ -785,6 +789,9 @@ async function persistActiveEntry(
|
|
|
785
789
|
await writeActiveEntry(cwd, sessionScope, entry.skill, entry, {
|
|
786
790
|
cwd,
|
|
787
791
|
audit: activeStateWriterAudit("write-active-entry", sessionScope),
|
|
792
|
+
...(typeof entry.committed_mode_state_revision === "number"
|
|
793
|
+
? { orderingRevision: entry.committed_mode_state_revision }
|
|
794
|
+
: {}),
|
|
788
795
|
});
|
|
789
796
|
}
|
|
790
797
|
}
|
|
@@ -863,6 +870,9 @@ export async function syncSkillActiveState(options: SyncSkillActiveStateOptions)
|
|
|
863
870
|
? { active_subskills: preservedActiveSubskills }
|
|
864
871
|
: {}),
|
|
865
872
|
...(typeof options.sourceRevision === "number" ? { source_state_revision: options.sourceRevision } : {}),
|
|
873
|
+
...(typeof options.committedModeRevision === "number"
|
|
874
|
+
? { committed_mode_state_revision: options.committedModeRevision }
|
|
875
|
+
: {}),
|
|
866
876
|
};
|
|
867
877
|
const sessionScope = { sessionId: options.sessionId };
|
|
868
878
|
await removeSupersededPlanningPipelineEntries(options.cwd, sessionScope, entry);
|
package/src/task/executor.ts
CHANGED
|
@@ -486,11 +486,12 @@ function buildSchemaViolationOutcome(
|
|
|
486
486
|
missing.length > 0
|
|
487
487
|
? `schema_violation: missing required fields: ${missing.join(", ")}`
|
|
488
488
|
: `schema_violation: ${failure.message}`;
|
|
489
|
+
const dataPreview = previewOffendingData(data);
|
|
489
490
|
const payload = {
|
|
490
491
|
error: "schema_violation",
|
|
491
492
|
message: failure.message,
|
|
492
493
|
missingRequired: missing,
|
|
493
|
-
data
|
|
494
|
+
data,
|
|
494
495
|
};
|
|
495
496
|
let rawOutput: string;
|
|
496
497
|
try {
|
|
@@ -498,7 +499,7 @@ function buildSchemaViolationOutcome(
|
|
|
498
499
|
} catch {
|
|
499
500
|
rawOutput = `{"error":"schema_violation","message":${JSON.stringify(headline)}}`;
|
|
500
501
|
}
|
|
501
|
-
return { rawOutput, stderr: headline
|
|
502
|
+
return { rawOutput, stderr: `${headline}. Offending data preview: ${dataPreview}`, exitCode: 1 };
|
|
502
503
|
}
|
|
503
504
|
|
|
504
505
|
function buildPlaceholderYieldOutcome(
|
|
@@ -541,9 +542,16 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
|
|
|
541
542
|
const completeData = normalizeCompleteData(submitData, reportFindings);
|
|
542
543
|
const { validator, error: schemaError } = buildOutputValidator(outputSchema);
|
|
543
544
|
if (schemaError) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
545
|
+
const outcome = buildSchemaViolationOutcome(
|
|
546
|
+
{
|
|
547
|
+
message: `invalid output schema: ${schemaError}`,
|
|
548
|
+
missingRequired: [],
|
|
549
|
+
},
|
|
550
|
+
completeData,
|
|
551
|
+
);
|
|
552
|
+
rawOutput = outcome.rawOutput;
|
|
553
|
+
stderr = outcome.stderr;
|
|
554
|
+
exitCode = outcome.exitCode;
|
|
547
555
|
} else {
|
|
548
556
|
const placeholderPath = findPlaceholderYieldPath(completeData);
|
|
549
557
|
if (placeholderPath) {
|
package/src/task/gjc-command.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
|
|
3
|
-
import { $
|
|
3
|
+
import { $pickenv } from "@gajae-code/utils";
|
|
4
4
|
|
|
5
5
|
interface GjcCommand {
|
|
6
6
|
cmd: string;
|
|
@@ -12,7 +12,7 @@ const DEFAULT_CMD = process.platform === "win32" ? "gjc.cmd" : "gjc";
|
|
|
12
12
|
const DEFAULT_SHELL = process.platform === "win32";
|
|
13
13
|
|
|
14
14
|
export function resolveGjcCommand(): GjcCommand {
|
|
15
|
-
const envCmd = $
|
|
15
|
+
const envCmd = $pickenv("GJC_SUBPROCESS_CMD", "PI_SUBPROCESS_CMD");
|
|
16
16
|
if (envCmd?.trim()) {
|
|
17
17
|
return { cmd: envCmd, args: [], shell: DEFAULT_SHELL };
|
|
18
18
|
}
|
package/src/task/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ import * as os from "node:os";
|
|
|
17
17
|
import path from "node:path";
|
|
18
18
|
import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from "@gajae-code/agent-core";
|
|
19
19
|
import type { Model, Usage } from "@gajae-code/ai";
|
|
20
|
-
import { $
|
|
20
|
+
import { $pickenv, prompt, Snowflake } from "@gajae-code/utils";
|
|
21
21
|
import type { ToolSession } from "..";
|
|
22
22
|
import { AsyncJobManager, OwnerSubagentShutdownError, type ResumeRunner } from "../async";
|
|
23
23
|
import { resolveAgentModelPatterns } from "../config/model-resolver";
|
|
@@ -43,6 +43,15 @@ import {
|
|
|
43
43
|
|
|
44
44
|
// Import review tools for side effects (registers subagent tool handlers)
|
|
45
45
|
import "../tools/review";
|
|
46
|
+
import {
|
|
47
|
+
assertExecutionRootMatchesRepositoryBinding,
|
|
48
|
+
assertPathUnderRepositoryBinding,
|
|
49
|
+
captureRepositoryBinding,
|
|
50
|
+
publicRepositoryBinding,
|
|
51
|
+
type RepositoryBinding,
|
|
52
|
+
RepositoryBindingError,
|
|
53
|
+
resolveTaskRepositoryBinding,
|
|
54
|
+
} from "../gjc-runtime/repository-binding";
|
|
46
55
|
import { initializeLocalRoot, type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
|
|
47
56
|
import { generateCommitMessage } from "../utils/commit-message-generator";
|
|
48
57
|
import * as git from "../utils/git";
|
|
@@ -128,6 +137,43 @@ function addUsageTotals(target: Usage, usage: Partial<Usage>): void {
|
|
|
128
137
|
target.cost.total += cost.total;
|
|
129
138
|
}
|
|
130
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Stamp/resolve repository authority for every task before agent discovery or spawn.
|
|
142
|
+
* Omitted bindings inherit the session worktree; declared ones must match it.
|
|
143
|
+
*/
|
|
144
|
+
async function resolveTaskItemsWithRepositoryBindings(
|
|
145
|
+
cwd: string,
|
|
146
|
+
tasks: readonly TaskItem[],
|
|
147
|
+
): Promise<{ tasks: TaskItem[]; error?: string }> {
|
|
148
|
+
const resolved: TaskItem[] = [];
|
|
149
|
+
for (const task of tasks) {
|
|
150
|
+
try {
|
|
151
|
+
const binding = await resolveTaskRepositoryBinding(cwd, task.repositoryBinding);
|
|
152
|
+
if (binding.relativeSubdir) {
|
|
153
|
+
assertPathUnderRepositoryBinding(binding, ".");
|
|
154
|
+
}
|
|
155
|
+
resolved.push({
|
|
156
|
+
...task,
|
|
157
|
+
repositoryBinding: binding,
|
|
158
|
+
});
|
|
159
|
+
} catch (error) {
|
|
160
|
+
const id = task.id?.trim() ? task.id : "(missing-id)";
|
|
161
|
+
if (error instanceof RepositoryBindingError) {
|
|
162
|
+
return { tasks: [], error: `Task "${id}" repository binding rejected: ${error.message}` };
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
tasks: [],
|
|
166
|
+
error: `Task "${id}" repository binding rejected: ${error instanceof Error ? error.message : String(error)}`,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { tasks: resolved };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function repositoryBindingFromTask(task: TaskItem): RepositoryBinding | undefined {
|
|
174
|
+
return task.repositoryBinding ? publicRepositoryBinding(task.repositoryBinding as RepositoryBinding) : undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
131
177
|
function validateTaskIdsForScheduling(tasks: readonly TaskItem[]): string | undefined {
|
|
132
178
|
const invalid: string[] = [];
|
|
133
179
|
for (let i = 0; i < tasks.length; i++) {
|
|
@@ -399,12 +445,16 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
399
445
|
this.session.getSessionSpawns() ?? "*",
|
|
400
446
|
);
|
|
401
447
|
}
|
|
448
|
+
readonly #sessionRepositoryBinding: RepositoryBinding;
|
|
449
|
+
|
|
402
450
|
private constructor(
|
|
403
451
|
private readonly session: ToolSession,
|
|
404
452
|
discoveredAgents: AgentDefinition[],
|
|
453
|
+
sessionRepositoryBinding: RepositoryBinding,
|
|
405
454
|
) {
|
|
406
|
-
this.#blockedAgent = $
|
|
455
|
+
this.#blockedAgent = $pickenv("GJC_BLOCKED_AGENT", "PI_BLOCKED_AGENT");
|
|
407
456
|
this.#discoveredAgents = discoveredAgents;
|
|
457
|
+
this.#sessionRepositoryBinding = sessionRepositoryBinding;
|
|
408
458
|
}
|
|
409
459
|
|
|
410
460
|
#getTaskSimpleMode(): TaskSimpleMode {
|
|
@@ -412,11 +462,16 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
412
462
|
}
|
|
413
463
|
|
|
414
464
|
/**
|
|
415
|
-
* Create a TaskTool instance
|
|
465
|
+
* Create a TaskTool instance.
|
|
466
|
+
* Repository authority is captured from session cwd *before* agent discovery so
|
|
467
|
+
* multi-repo workspaces fail closed prior to context/discovery (#2901).
|
|
416
468
|
*/
|
|
417
469
|
static async create(session: ToolSession): Promise<TaskTool> {
|
|
470
|
+
const sessionRepositoryBinding = await captureRepositoryBinding(session.cwd, { displayPath: session.cwd });
|
|
471
|
+
// Authority check before discovery: session cwd must resolve to a stable binding.
|
|
472
|
+
await assertExecutionRootMatchesRepositoryBinding(session.cwd, sessionRepositoryBinding);
|
|
418
473
|
const { agents } = await discoverAgents(session.cwd);
|
|
419
|
-
return new TaskTool(session, agents);
|
|
474
|
+
return new TaskTool(session, agents, publicRepositoryBinding(sessionRepositoryBinding));
|
|
420
475
|
}
|
|
421
476
|
|
|
422
477
|
async execute(
|
|
@@ -432,13 +487,32 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
432
487
|
return createTaskModeError(validationError);
|
|
433
488
|
}
|
|
434
489
|
|
|
435
|
-
|
|
436
|
-
|
|
490
|
+
// Re-verify session authority before using any discovered agents or scheduling work.
|
|
491
|
+
try {
|
|
492
|
+
await assertExecutionRootMatchesRepositoryBinding(this.session.cwd, this.#sessionRepositoryBinding);
|
|
493
|
+
} catch (error) {
|
|
494
|
+
const message =
|
|
495
|
+
error instanceof RepositoryBindingError
|
|
496
|
+
? error.message
|
|
497
|
+
: error instanceof Error
|
|
498
|
+
? error.message
|
|
499
|
+
: String(error);
|
|
500
|
+
return createTaskModeError(`Session repository binding rejected before task discovery: ${message}`);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const rawTaskItems = params.tasks ?? [];
|
|
504
|
+
const taskIdValidationError = validateTaskIdsForScheduling(rawTaskItems);
|
|
437
505
|
if (taskIdValidationError) {
|
|
438
506
|
return createTaskModeError(taskIdValidationError);
|
|
439
507
|
}
|
|
508
|
+
const bindingResolution = await resolveTaskItemsWithRepositoryBindings(this.session.cwd, rawTaskItems);
|
|
509
|
+
if (bindingResolution.error) {
|
|
510
|
+
return createTaskModeError(bindingResolution.error);
|
|
511
|
+
}
|
|
512
|
+
const taskItems = bindingResolution.tasks;
|
|
513
|
+
const paramsWithBindings: TaskParams = { ...params, tasks: taskItems };
|
|
440
514
|
if (taskItems.length === 0) {
|
|
441
|
-
return this.#executeSync(_toolCallId,
|
|
515
|
+
return this.#executeSync(_toolCallId, paramsWithBindings, signal, onUpdate);
|
|
442
516
|
}
|
|
443
517
|
const agent = getAgent(this.#discoveredAgents, params.agent);
|
|
444
518
|
if (!agent) {
|
|
@@ -926,13 +1000,31 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
926
1000
|
},
|
|
927
1001
|
): Promise<AgentToolResult<TaskToolDetails>> {
|
|
928
1002
|
const startTime = Date.now();
|
|
1003
|
+
// Pre-discovery authority: session cwd must still match the capture from create().
|
|
1004
|
+
try {
|
|
1005
|
+
await assertExecutionRootMatchesRepositoryBinding(this.session.cwd, this.#sessionRepositoryBinding);
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
const message =
|
|
1008
|
+
error instanceof RepositoryBindingError
|
|
1009
|
+
? error.message
|
|
1010
|
+
: error instanceof Error
|
|
1011
|
+
? error.message
|
|
1012
|
+
: String(error);
|
|
1013
|
+
return createTaskModeError(`Session repository binding rejected before task discovery: ${message}`);
|
|
1014
|
+
}
|
|
1015
|
+
const bindingResolution = await resolveTaskItemsWithRepositoryBindings(this.session.cwd, params.tasks ?? []);
|
|
1016
|
+
if (bindingResolution.error) {
|
|
1017
|
+
return createTaskModeError(bindingResolution.error);
|
|
1018
|
+
}
|
|
1019
|
+
const boundParams: TaskParams = { ...params, tasks: bindingResolution.tasks };
|
|
1020
|
+
|
|
929
1021
|
const { agents, projectAgentsDir } = await discoverAgents(this.session.cwd);
|
|
930
|
-
const { agent: agentName, context, schema: outputSchema } =
|
|
1022
|
+
const { agent: agentName, context, schema: outputSchema } = boundParams;
|
|
931
1023
|
const simpleMode = this.#getTaskSimpleMode();
|
|
932
1024
|
const { contextEnabled, customSchemaEnabled } = getTaskSimpleModeCapabilities(simpleMode);
|
|
933
1025
|
const sharedContext = contextEnabled ? context?.trim() : undefined;
|
|
934
1026
|
const isolationMode = this.session.settings.get("task.isolation.mode");
|
|
935
|
-
const isolationRequested = "isolated" in
|
|
1027
|
+
const isolationRequested = "isolated" in boundParams ? boundParams.isolated === true : false;
|
|
936
1028
|
const isIsolated = isolationMode !== "none" && isolationRequested;
|
|
937
1029
|
const mergeMode = this.session.settings.get("task.isolation.merge");
|
|
938
1030
|
const commitStyle = this.session.settings.get("task.isolation.commits");
|
|
@@ -940,7 +1032,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
940
1032
|
const taskDepth = this.session.taskDepth ?? 0;
|
|
941
1033
|
const subagentLspEnabled = (this.session.enableLsp ?? true) && this.session.settings.get("task.enableLsp");
|
|
942
1034
|
|
|
943
|
-
if (isolationMode === "none" && "isolated" in
|
|
1035
|
+
if (isolationMode === "none" && "isolated" in boundParams) {
|
|
944
1036
|
return {
|
|
945
1037
|
content: [
|
|
946
1038
|
{
|
|
@@ -1000,7 +1092,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1000
1092
|
}
|
|
1001
1093
|
|
|
1002
1094
|
const forkContextValidationError = validateForkContextRequests(
|
|
1003
|
-
|
|
1095
|
+
boundParams.tasks ?? [],
|
|
1004
1096
|
agent,
|
|
1005
1097
|
this.session.settings.get("task.forkContext.enabled"),
|
|
1006
1098
|
);
|
|
@@ -1039,7 +1131,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1039
1131
|
: (effectiveAgent.output ?? this.session.outputSchema);
|
|
1040
1132
|
|
|
1041
1133
|
// Handle empty or missing tasks
|
|
1042
|
-
if (!
|
|
1134
|
+
if (!boundParams.tasks || boundParams.tasks.length === 0) {
|
|
1043
1135
|
return {
|
|
1044
1136
|
content: [
|
|
1045
1137
|
{
|
|
@@ -1057,7 +1149,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1057
1149
|
};
|
|
1058
1150
|
}
|
|
1059
1151
|
|
|
1060
|
-
const tasks =
|
|
1152
|
+
const tasks = boundParams.tasks;
|
|
1061
1153
|
const missingTaskIndexes: number[] = [];
|
|
1062
1154
|
const idIndexes = new Map<string, number[]>();
|
|
1063
1155
|
|
|
@@ -1173,7 +1265,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1173
1265
|
const emitProgress = () => {
|
|
1174
1266
|
const progress = Array.from(progressMap.values()).sort((a, b) => a.index - b.index);
|
|
1175
1267
|
onUpdate?.({
|
|
1176
|
-
content: [{ type: "text", text: `Running ${
|
|
1268
|
+
content: [{ type: "text", text: `Running ${tasks.length} agents...` }],
|
|
1177
1269
|
details: {
|
|
1178
1270
|
projectAgentsDir,
|
|
1179
1271
|
results: [],
|
|
@@ -1342,7 +1434,13 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1342
1434
|
const taskSessionFile = managedPersistence
|
|
1343
1435
|
? null
|
|
1344
1436
|
: (overrides?.sessionFile ?? executionOverrides?.sessionFiles?.get(task.id) ?? null);
|
|
1437
|
+
const taskRepositoryBinding = repositoryBindingFromTask(task) ?? this.#sessionRepositoryBinding;
|
|
1438
|
+
// Declared paths (relativeSubdir) must stay under the bound root before spawn.
|
|
1439
|
+
if (taskRepositoryBinding.relativeSubdir) {
|
|
1440
|
+
assertPathUnderRepositoryBinding(taskRepositoryBinding, ".");
|
|
1441
|
+
}
|
|
1345
1442
|
if (!isIsolated) {
|
|
1443
|
+
await assertExecutionRootMatchesRepositoryBinding(this.session.cwd, taskRepositoryBinding);
|
|
1346
1444
|
const result = await runSubprocess({
|
|
1347
1445
|
cwd: this.session.cwd,
|
|
1348
1446
|
agent: effectiveAgent,
|
|
@@ -1393,7 +1491,12 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1393
1491
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
1394
1492
|
forkContextSeed,
|
|
1395
1493
|
});
|
|
1396
|
-
return {
|
|
1494
|
+
return {
|
|
1495
|
+
...result,
|
|
1496
|
+
...(forkContext ? { forkContext } : {}),
|
|
1497
|
+
forkContextAdvisory,
|
|
1498
|
+
repositoryBinding: publicRepositoryBinding(taskRepositoryBinding),
|
|
1499
|
+
};
|
|
1397
1500
|
}
|
|
1398
1501
|
|
|
1399
1502
|
const taskStart = Date.now();
|
|
@@ -1406,6 +1509,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1406
1509
|
|
|
1407
1510
|
isolationHandle = await ensureIsolation(repoRoot, task.id, preferredIsolationBackend);
|
|
1408
1511
|
const isolationDir = isolationHandle.mergedDir;
|
|
1512
|
+
// Isolated worktrees must preserve the source repository identity (#2901).
|
|
1513
|
+
await assertExecutionRootMatchesRepositoryBinding(isolationDir, taskRepositoryBinding);
|
|
1409
1514
|
|
|
1410
1515
|
const result = await runSubprocess({
|
|
1411
1516
|
cwd: this.session.cwd,
|
|
@@ -1462,6 +1567,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1462
1567
|
...result,
|
|
1463
1568
|
...(forkContext ? { forkContext } : {}),
|
|
1464
1569
|
forkContextAdvisory,
|
|
1570
|
+
repositoryBinding: publicRepositoryBinding(taskRepositoryBinding),
|
|
1465
1571
|
};
|
|
1466
1572
|
if (mergeMode === "branch" && resultWithForkContext.exitCode === 0) {
|
|
1467
1573
|
try {
|
package/src/task/receipt.ts
CHANGED
|
@@ -48,6 +48,8 @@ export interface TaskResultReceipt {
|
|
|
48
48
|
extractedToolCounts?: Record<string, number>;
|
|
49
49
|
forkContext?: SingleResult["forkContext"];
|
|
50
50
|
forkContextAdvisory?: SingleResult["forkContextAdvisory"];
|
|
51
|
+
/** Resolved repository identity for this delegated lane (#2901). */
|
|
52
|
+
repositoryBinding?: SingleResult["repositoryBinding"];
|
|
51
53
|
roi?: TaskRoi;
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -262,6 +264,7 @@ export function buildTaskReceipt(raw: SingleResult): TaskResultReceipt {
|
|
|
262
264
|
extractedToolCounts,
|
|
263
265
|
forkContext: raw.forkContext,
|
|
264
266
|
forkContextAdvisory: raw.forkContextAdvisory,
|
|
267
|
+
repositoryBinding: raw.repositoryBinding,
|
|
265
268
|
roi: buildTaskRoi(raw),
|
|
266
269
|
};
|
|
267
270
|
}
|