@gajae-code/coding-agent 0.11.7 → 0.11.8
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 +55 -0
- package/dist/types/cli/args.d.ts +0 -2
- 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/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/repository-binding.d.ts +63 -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/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 +2 -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/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 +5 -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 +13 -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 +11 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +17 -2
- package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
- package/dist/types/session/session-storage.d.ts +18 -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/notify-cli.ts +5 -38
- package/src/cli.ts +18 -0
- package/src/commands/sdk.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-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 +2 -2
- 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/launch-tmux.ts +1 -1
- package/src/gjc-runtime/managed-owner-supervisor.ts +83 -4
- package/src/gjc-runtime/ralplan-runtime.ts +75 -3
- package/src/gjc-runtime/repository-binding.ts +267 -0
- package/src/gjc-runtime/session-state-sidecar.ts +25 -9
- 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/internal-urls/docs-index.generated.ts +6 -5
- 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 +22 -18
- 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 +41 -27
- 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 +6 -4
- package/src/prompts/tools/cron.md +3 -1
- 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/transports/http.ts +13 -6
- package/src/sdk/broker/broker.ts +34 -20
- package/src/sdk/broker/lifecycle.ts +185 -36
- package/src/sdk/bus/chat-daemon-control.ts +18 -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 +13 -3
- package/src/sdk/bus/telegram-daemon-control.ts +145 -33
- package/src/sdk/bus/telegram-daemon.ts +835 -110
- 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 +52 -5
- package/src/session/blob-store.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +854 -84
- package/src/session/internal/managed-session-storage.ts +251 -199
- package/src/session/internal/native-publish-outcome.ts +266 -0
- package/src/session/session-manager.ts +310 -327
- package/src/session/session-storage.ts +128 -11
- 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 +198 -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
|
@@ -259,6 +259,19 @@ export interface VerifiedSessionDeleteTarget {
|
|
|
259
259
|
detachedArtifactsPath?: string;
|
|
260
260
|
/** Identity-bound quarantine path retained when transcript unlink deferred cleanup. */
|
|
261
261
|
detachedTranscriptPath?: string;
|
|
262
|
+
/** Native-retained publisher successor observed during transcript cleanup. */
|
|
263
|
+
retainedTranscriptSuccessorPath?: string;
|
|
264
|
+
/** Native-retained exchange placeholder observed during transcript cleanup. */
|
|
265
|
+
retainedTranscriptPlaceholderPath?: string;
|
|
266
|
+
/** Native-retained transcript entry whose identity could not be verified. */
|
|
267
|
+
retainedTranscriptUnknownPath?: string;
|
|
268
|
+
/** Native-retained publisher successor observed during cleanup. */
|
|
269
|
+
retainedArtifactsSuccessorPath?: string;
|
|
270
|
+
/** Native-retained exchange placeholder observed during cleanup. */
|
|
271
|
+
retainedArtifactsPlaceholderPath?: string;
|
|
272
|
+
/** Native-retained entry whose identity could not be verified. */
|
|
273
|
+
retainedArtifactsUnknownPath?: string;
|
|
274
|
+
|
|
262
275
|
/** Caller-published, no-replace quarantine pathname for the next artifact detach. */
|
|
263
276
|
plannedArtifactsPath?: string;
|
|
264
277
|
/** Caller-published, no-replace quarantine pathname for the next transcript detach. */
|
|
@@ -288,6 +301,9 @@ export type VerifiedSessionDeleteResult =
|
|
|
288
301
|
artifactsTree: NativeDirectoryTreeSnapshot;
|
|
289
302
|
/** Transcript identity (unchanged) for retry binding. */
|
|
290
303
|
transcriptIdentity: SessionStorageFileIdentity;
|
|
304
|
+
retainedSuccessorPath?: string;
|
|
305
|
+
retainedPlaceholderPath?: string;
|
|
306
|
+
retainedUnknownPath?: string;
|
|
291
307
|
}
|
|
292
308
|
| {
|
|
293
309
|
kind: "cleanup_pending";
|
|
@@ -297,6 +313,9 @@ export type VerifiedSessionDeleteResult =
|
|
|
297
313
|
transcriptIdentity: SessionStorageFileIdentity;
|
|
298
314
|
/** Optional identity-bound transcript quarantine path for restart cleanup. */
|
|
299
315
|
detachedTranscriptPath?: string;
|
|
316
|
+
retainedSuccessorPath?: string;
|
|
317
|
+
retainedPlaceholderPath?: string;
|
|
318
|
+
retainedUnknownPath?: string;
|
|
300
319
|
};
|
|
301
320
|
|
|
302
321
|
/** Default OS-close dispatcher: a direct `fs.closeSync`. */
|
|
@@ -306,7 +325,22 @@ const defaultCloseAdapter: SessionStorageWriterCloseAdapter = {
|
|
|
306
325
|
},
|
|
307
326
|
};
|
|
308
327
|
|
|
309
|
-
type NativeExactUnlinkResult =
|
|
328
|
+
type NativeExactUnlinkResult =
|
|
329
|
+
| {
|
|
330
|
+
ok: true;
|
|
331
|
+
detachedPath?: string;
|
|
332
|
+
retainedSuccessorPath?: string;
|
|
333
|
+
retainedPlaceholderPath?: string;
|
|
334
|
+
retainedUnknownPath?: string;
|
|
335
|
+
}
|
|
336
|
+
| {
|
|
337
|
+
ok: false;
|
|
338
|
+
code: string;
|
|
339
|
+
detachedPath?: string;
|
|
340
|
+
retainedSuccessorPath?: string;
|
|
341
|
+
retainedPlaceholderPath?: string;
|
|
342
|
+
retainedUnknownPath?: string;
|
|
343
|
+
};
|
|
310
344
|
type NativeExactUnlink = (
|
|
311
345
|
path: string,
|
|
312
346
|
identity: {
|
|
@@ -766,7 +800,13 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
766
800
|
expectedArtifactsIdentity,
|
|
767
801
|
expectedArtifactsTree,
|
|
768
802
|
detachedArtifactsPath,
|
|
803
|
+
retainedArtifactsSuccessorPath,
|
|
804
|
+
retainedArtifactsPlaceholderPath,
|
|
805
|
+
retainedArtifactsUnknownPath,
|
|
769
806
|
detachedTranscriptPath,
|
|
807
|
+
retainedTranscriptSuccessorPath,
|
|
808
|
+
retainedTranscriptPlaceholderPath,
|
|
809
|
+
retainedTranscriptUnknownPath,
|
|
770
810
|
plannedArtifactsPath,
|
|
771
811
|
plannedTranscriptPath,
|
|
772
812
|
artifactsRemoved,
|
|
@@ -847,7 +887,8 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
847
887
|
if (
|
|
848
888
|
!expectedArtifactsIdentity ||
|
|
849
889
|
path.dirname(detachedArtifactsPath) !== path.dirname(transcriptPath) ||
|
|
850
|
-
!path.basename(detachedArtifactsPath).startsWith(".gjc-delete-")
|
|
890
|
+
(!path.basename(detachedArtifactsPath).startsWith(".gjc-delete-") &&
|
|
891
|
+
!detachedArtifactsPath.endsWith(".removing"))
|
|
851
892
|
) {
|
|
852
893
|
throw new SessionDeleteVerificationError("artifacts", "Detached artifact cleanup evidence is invalid");
|
|
853
894
|
}
|
|
@@ -882,17 +923,20 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
882
923
|
artifactsIdentity: expectedArtifactsIdentity,
|
|
883
924
|
detachedArtifactsPath: retainedRoot,
|
|
884
925
|
artifactsTree: expectedArtifactsTree,
|
|
926
|
+
...((removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath)
|
|
927
|
+
? { retainedSuccessorPath: removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath }
|
|
928
|
+
: {}),
|
|
929
|
+
...((removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath)
|
|
930
|
+
? { retainedPlaceholderPath: removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath }
|
|
931
|
+
: {}),
|
|
932
|
+
...((removal.retainedUnknownPath ?? retainedArtifactsUnknownPath)
|
|
933
|
+
? { retainedUnknownPath: removal.retainedUnknownPath ?? retainedArtifactsUnknownPath }
|
|
934
|
+
: {}),
|
|
885
935
|
transcriptIdentity,
|
|
886
936
|
};
|
|
887
937
|
}
|
|
888
938
|
}
|
|
889
939
|
|
|
890
|
-
if (artifactsRemoved && (detachedArtifactsPath || expectedArtifactsIdentity)) {
|
|
891
|
-
throw new SessionDeleteVerificationError(
|
|
892
|
-
"artifacts",
|
|
893
|
-
"Artifact phase receipt conflicts with pending artifact cleanup",
|
|
894
|
-
);
|
|
895
|
-
}
|
|
896
940
|
const artifactsDir = transcriptPath.slice(0, -6);
|
|
897
941
|
const artifactsIdentity = this.#optionalDirectoryIdentity(artifactsDir);
|
|
898
942
|
if (artifactsRemoved && artifactsIdentity) {
|
|
@@ -947,12 +991,48 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
947
991
|
directory: true,
|
|
948
992
|
quarantineName: path.basename(plannedArtifactsPath),
|
|
949
993
|
});
|
|
950
|
-
if (!detach.
|
|
994
|
+
if (!detach.detachedPath) {
|
|
951
995
|
throw new SessionDeleteVerificationError(
|
|
952
996
|
"artifacts",
|
|
953
997
|
`Exact artifact detach rejected: ${detach.ok ? "missing_path" : detach.code}`,
|
|
954
998
|
);
|
|
955
999
|
}
|
|
1000
|
+
if (!detach.ok && process.platform !== "win32") {
|
|
1001
|
+
let descriptor: number | undefined;
|
|
1002
|
+
try {
|
|
1003
|
+
descriptor = fs.openSync(
|
|
1004
|
+
path.dirname(transcriptPath),
|
|
1005
|
+
fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW,
|
|
1006
|
+
);
|
|
1007
|
+
const durableParent = fs.fstatSync(descriptor, { bigint: true });
|
|
1008
|
+
if (
|
|
1009
|
+
!durableParent.isDirectory() ||
|
|
1010
|
+
durableParent.dev !== parentIdentity.dev ||
|
|
1011
|
+
durableParent.ino !== parentIdentity.ino
|
|
1012
|
+
)
|
|
1013
|
+
throw new Error("parent_changed");
|
|
1014
|
+
fs.fsyncSync(descriptor);
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
throw new SessionDeleteVerificationError("artifacts", "durability_failed", { cause: toError(error) });
|
|
1017
|
+
} finally {
|
|
1018
|
+
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
if (!detach.ok) {
|
|
1022
|
+
return {
|
|
1023
|
+
kind: "cleanup_pending",
|
|
1024
|
+
phase: "artifacts",
|
|
1025
|
+
error: new SessionDeleteVerificationError("artifacts", `Exact artifact detach retained: ${detach.code}`),
|
|
1026
|
+
artifactsIdentity,
|
|
1027
|
+
detachedArtifactsPath: detach.detachedPath,
|
|
1028
|
+
artifactsTree,
|
|
1029
|
+
...(detach.retainedSuccessorPath ? { retainedSuccessorPath: detach.retainedSuccessorPath } : {}),
|
|
1030
|
+
...(detach.retainedPlaceholderPath ? { retainedPlaceholderPath: detach.retainedPlaceholderPath } : {}),
|
|
1031
|
+
...(detach.retainedUnknownPath ? { retainedUnknownPath: detach.retainedUnknownPath } : {}),
|
|
1032
|
+
|
|
1033
|
+
transcriptIdentity,
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
956
1036
|
const removal = removeDirectoryTreeExact(detach.detachedPath, artifactsTree);
|
|
957
1037
|
if (!removal.ok) {
|
|
958
1038
|
const retainedRoot = removal.detachedPath ?? detach.detachedPath;
|
|
@@ -971,6 +1051,15 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
971
1051
|
artifactsIdentity,
|
|
972
1052
|
detachedArtifactsPath: retainedRoot,
|
|
973
1053
|
artifactsTree,
|
|
1054
|
+
...((removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath)
|
|
1055
|
+
? { retainedSuccessorPath: removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath }
|
|
1056
|
+
: {}),
|
|
1057
|
+
...((removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath)
|
|
1058
|
+
? { retainedPlaceholderPath: removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath }
|
|
1059
|
+
: {}),
|
|
1060
|
+
...((removal.retainedUnknownPath ?? retainedArtifactsUnknownPath)
|
|
1061
|
+
? { retainedUnknownPath: removal.retainedUnknownPath ?? retainedArtifactsUnknownPath }
|
|
1062
|
+
: {}),
|
|
974
1063
|
transcriptIdentity,
|
|
975
1064
|
};
|
|
976
1065
|
}
|
|
@@ -1010,13 +1099,27 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
1010
1099
|
});
|
|
1011
1100
|
if (!deletion.ok) {
|
|
1012
1101
|
const error = exactUnlinkFailure(deletion);
|
|
1013
|
-
|
|
1102
|
+
const retainedAuthority =
|
|
1103
|
+
deletion.detachedPath ||
|
|
1104
|
+
deletion.retainedSuccessorPath ||
|
|
1105
|
+
deletion.retainedPlaceholderPath ||
|
|
1106
|
+
deletion.retainedUnknownPath;
|
|
1107
|
+
if ((error.kind === "identity" || error.kind === "symlink") && !retainedAuthority) throw error;
|
|
1014
1108
|
return {
|
|
1015
1109
|
kind: "cleanup_pending",
|
|
1016
1110
|
phase: "transcript",
|
|
1017
1111
|
error,
|
|
1018
1112
|
transcriptIdentity,
|
|
1019
1113
|
detachedTranscriptPath: deletion.detachedPath ?? detachedTranscriptPath,
|
|
1114
|
+
...((deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath)
|
|
1115
|
+
? { retainedSuccessorPath: deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath }
|
|
1116
|
+
: {}),
|
|
1117
|
+
...((deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath)
|
|
1118
|
+
? { retainedPlaceholderPath: deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath }
|
|
1119
|
+
: {}),
|
|
1120
|
+
...((deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath)
|
|
1121
|
+
? { retainedUnknownPath: deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath }
|
|
1122
|
+
: {}),
|
|
1020
1123
|
};
|
|
1021
1124
|
}
|
|
1022
1125
|
return { kind: "deleted" };
|
|
@@ -1058,13 +1161,27 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
1058
1161
|
});
|
|
1059
1162
|
if (!deletion.ok) {
|
|
1060
1163
|
const error = exactUnlinkFailure(deletion);
|
|
1061
|
-
|
|
1164
|
+
const retainedAuthority =
|
|
1165
|
+
deletion.detachedPath ||
|
|
1166
|
+
deletion.retainedSuccessorPath ||
|
|
1167
|
+
deletion.retainedPlaceholderPath ||
|
|
1168
|
+
deletion.retainedUnknownPath;
|
|
1169
|
+
if ((error.kind === "identity" || error.kind === "symlink") && !retainedAuthority) throw error;
|
|
1062
1170
|
return {
|
|
1063
1171
|
kind: "cleanup_pending",
|
|
1064
1172
|
phase: "transcript",
|
|
1065
1173
|
error,
|
|
1066
1174
|
transcriptIdentity,
|
|
1067
1175
|
detachedTranscriptPath: deletion.detachedPath,
|
|
1176
|
+
...((deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath)
|
|
1177
|
+
? { retainedSuccessorPath: deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath }
|
|
1178
|
+
: {}),
|
|
1179
|
+
...((deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath)
|
|
1180
|
+
? { retainedPlaceholderPath: deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath }
|
|
1181
|
+
: {}),
|
|
1182
|
+
...((deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath)
|
|
1183
|
+
? { retainedUnknownPath: deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath }
|
|
1184
|
+
: {}),
|
|
1068
1185
|
};
|
|
1069
1186
|
}
|
|
1070
1187
|
return { kind: "deleted" };
|
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
|
}
|
package/src/task/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@gajae-code/agent-core";
|
|
2
2
|
import type { Usage } from "@gajae-code/ai";
|
|
3
|
-
import { $
|
|
3
|
+
import { $pickenv } from "@gajae-code/utils";
|
|
4
4
|
import * as z from "zod/v4";
|
|
5
5
|
import { isValidTaskId, TASK_ID_DESCRIPTION } from "./id";
|
|
6
6
|
import type { TaskResultReceipt } from "./receipt";
|
|
@@ -27,10 +27,10 @@ const parseNumber = (value: string | undefined, defaultValue: number): number =>
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
/** Maximum output bytes per agent */
|
|
30
|
-
export const MAX_OUTPUT_BYTES = parseNumber($
|
|
30
|
+
export const MAX_OUTPUT_BYTES = parseNumber($pickenv("GJC_TASK_MAX_OUTPUT_BYTES", "PI_TASK_MAX_OUTPUT_BYTES"), 500_000);
|
|
31
31
|
|
|
32
32
|
/** Maximum output lines per agent */
|
|
33
|
-
export const MAX_OUTPUT_LINES = parseNumber($
|
|
33
|
+
export const MAX_OUTPUT_LINES = parseNumber($pickenv("GJC_TASK_MAX_OUTPUT_LINES", "PI_TASK_MAX_OUTPUT_LINES"), 5000);
|
|
34
34
|
|
|
35
35
|
/** EventBus channel for raw subagent events */
|
|
36
36
|
export const TASK_SUBAGENT_EVENT_CHANNEL = "task:subagent:event";
|
|
@@ -74,6 +74,19 @@ const spawnPlanSchema = z
|
|
|
74
74
|
})
|
|
75
75
|
.describe("justification required before spawning more than four tasks");
|
|
76
76
|
|
|
77
|
+
const repositoryBindingSchema = z
|
|
78
|
+
.object({
|
|
79
|
+
schema: z.literal("gjc.repository_binding.v1"),
|
|
80
|
+
worktreeRoot: z.string().min(1).describe("canonical git worktree root"),
|
|
81
|
+
commonDir: z.string().min(1).nullable().describe("git common dir, or null outside a git checkout"),
|
|
82
|
+
relativeSubdir: z.string().min(1).optional().describe("optional repo-relative subdirectory; not an absolute cwd"),
|
|
83
|
+
displayPath: z.string().min(1).optional().describe("human-facing path; never used for authority"),
|
|
84
|
+
head: z.string().min(1).optional(),
|
|
85
|
+
branch: z.string().min(1).optional(),
|
|
86
|
+
})
|
|
87
|
+
.strict()
|
|
88
|
+
.describe("authoritative repository identity for multi-repo fail-closed spawn");
|
|
89
|
+
|
|
77
90
|
const createTaskItemSchema = (_contextEnabled: boolean) =>
|
|
78
91
|
z.object({
|
|
79
92
|
id: z.string().max(48).refine(isValidTaskId, TASK_ID_DESCRIPTION).describe("filesystem-safe task identifier"),
|
|
@@ -85,6 +98,11 @@ const createTaskItemSchema = (_contextEnabled: boolean) =>
|
|
|
85
98
|
.describe(
|
|
86
99
|
"fork-context mode: none/omitted copies no parent context; receipt copies a minimal receipt-sized snapshot; last-turn copies only the latest exchange; bounded copies the bounded default snapshot; full copies a larger sanitized snapshot up to the configured/model token cap",
|
|
87
100
|
),
|
|
101
|
+
repositoryBinding: repositoryBindingSchema
|
|
102
|
+
.optional()
|
|
103
|
+
.describe(
|
|
104
|
+
"authoritative repository identity; omitted items are stamped from session cwd before discovery/spawn and still fail closed on sibling drift",
|
|
105
|
+
),
|
|
88
106
|
});
|
|
89
107
|
|
|
90
108
|
/** Single task item for parallel execution (default shape with context enabled). */
|
|
@@ -353,6 +371,19 @@ export interface SingleResult {
|
|
|
353
371
|
* never changes the actual mode selection).
|
|
354
372
|
*/
|
|
355
373
|
forkContextAdvisory?: { recommendedMode: ForkContextMode; reasons: string[] };
|
|
374
|
+
/**
|
|
375
|
+
* Resolved repository identity used for this task after pre-discovery stamping
|
|
376
|
+
* and fail-closed validation (#2901).
|
|
377
|
+
*/
|
|
378
|
+
repositoryBinding?: {
|
|
379
|
+
schema: "gjc.repository_binding.v1";
|
|
380
|
+
worktreeRoot: string;
|
|
381
|
+
commonDir: string | null;
|
|
382
|
+
relativeSubdir?: string;
|
|
383
|
+
displayPath?: string;
|
|
384
|
+
head?: string;
|
|
385
|
+
branch?: string;
|
|
386
|
+
};
|
|
356
387
|
}
|
|
357
388
|
|
|
358
389
|
/** True only for complete, factual five-bucket cost accounting. */
|