@gajae-code/coding-agent 0.16.4 → 0.16.6
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 +54 -0
- package/dist/types/capability/types.d.ts +4 -0
- package/dist/types/cli/customize-doctor.d.ts +22 -0
- package/dist/types/config/file-lock.d.ts +31 -3
- package/dist/types/coordinator-mcp/question-state.d.ts +7 -0
- package/dist/types/coordinator-mcp/server.d.ts +7 -0
- package/dist/types/discovery/helpers.d.ts +33 -0
- package/dist/types/exec/bash-executor.d.ts +17 -1
- package/dist/types/exec/bash-shell-guardian.d.ts +15 -0
- package/dist/types/exec/bash-shell-runtime-entry.d.ts +2 -0
- package/dist/types/exec/bash-shell-signals.d.ts +2 -0
- package/dist/types/exec/bash-shell-supervisor-entry.d.ts +1 -0
- package/dist/types/exec/bash-shell-supervisor.d.ts +10 -0
- package/dist/types/exec/bash-shell-worker-entry.d.ts +2 -0
- package/dist/types/exec/bash-shell-worker-protocol.d.ts +63 -0
- package/dist/types/exec/bash-shell-worker.d.ts +1 -0
- package/dist/types/exec/isolated-shell.d.ts +19 -0
- package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +9 -0
- package/dist/types/extensibility/extensions/function-hooks.d.ts +169 -0
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/runner.d.ts +46 -6
- package/dist/types/extensibility/extensions/types.d.ts +16 -0
- package/dist/types/extensibility/extensions/wrapper.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/constrained-hooks.d.ts +35 -2
- package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
- package/dist/types/extensibility/hooks/types.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +4 -1
- package/dist/types/extensibility/skill-management.d.ts +9 -4
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +4 -1
- package/dist/types/gjc-runtime/linux-proc.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +12 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +41 -3
- package/dist/types/gjc-runtime/ultragoal-succession.d.ts +251 -0
- package/dist/types/modes/components/bash-execution.d.ts +1 -0
- package/dist/types/modes/components/execution-shared.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/types.d.ts +6 -0
- package/dist/types/sdk/broker/ensure.d.ts +32 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +0 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
- package/dist/types/sdk/broker/startup-failure.d.ts +4 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +17 -2
- package/dist/types/sdk/bus/conversation-store.d.ts +11 -0
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +8 -8
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/topic-registry.d.ts +8 -0
- package/dist/types/sdk/client/client.d.ts +6 -0
- package/dist/types/sdk/host/host.d.ts +3 -1
- package/dist/types/sdk/host/session-runtime.d.ts +4 -2
- package/dist/types/sdk/lifecycle/service.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +10 -2
- package/dist/types/sdk/session.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/setup/paseo/announce.d.ts +4 -3
- package/dist/types/skill-state/canonical-skills.d.ts +4 -0
- package/dist/types/system-prompt.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/github-cache.d.ts +2 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/package.json +9 -7
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/src/capability/index.ts +14 -2
- package/src/capability/types.ts +4 -0
- package/src/cli/customize-doctor.ts +155 -12
- package/src/cli.ts +22 -0
- package/src/commands/sdk.ts +74 -31
- package/src/config/file-lock-gc.ts +40 -2
- package/src/config/file-lock.ts +198 -14
- package/src/config/model-preset-registry.ts +115 -16
- package/src/config/model-registry.ts +16 -12
- package/src/coordinator-mcp/question-state.ts +33 -2
- package/src/coordinator-mcp/server.ts +477 -101
- package/src/crash/sanitize.ts +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -0
- package/src/discovery/builtin.ts +50 -62
- package/src/discovery/helpers.ts +297 -32
- package/src/exec/bash-executor.ts +330 -50
- package/src/exec/bash-shell-guardian.ts +388 -0
- package/src/exec/bash-shell-runtime-entry.ts +5 -0
- package/src/exec/bash-shell-signals.ts +29 -0
- package/src/exec/bash-shell-supervisor-entry.ts +3 -0
- package/src/exec/bash-shell-supervisor.ts +359 -0
- package/src/exec/bash-shell-worker-entry.ts +5 -0
- package/src/exec/bash-shell-worker-protocol.ts +36 -0
- package/src/exec/bash-shell-worker.ts +116 -0
- package/src/exec/isolated-shell.ts +393 -0
- package/src/extensibility/extensions/function-hooks-internal.ts +73 -0
- package/src/extensibility/extensions/function-hooks.ts +959 -0
- package/src/extensibility/extensions/index.ts +22 -0
- package/src/extensibility/extensions/loader.ts +53 -2
- package/src/extensibility/extensions/runner.ts +895 -264
- package/src/extensibility/extensions/types.ts +42 -0
- package/src/extensibility/extensions/wrapper.ts +56 -15
- package/src/extensibility/gjc-plugins/compiler.ts +20 -2
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
- package/src/extensibility/gjc-plugins/registry.ts +113 -1
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +20 -2
- package/src/extensibility/gjc-plugins/schema.ts +121 -4
- package/src/extensibility/gjc-plugins/types.ts +25 -0
- package/src/extensibility/hooks/loader.ts +133 -16
- package/src/extensibility/hooks/types.ts +28 -0
- package/src/extensibility/runtime-skill-discovery.ts +80 -47
- package/src/extensibility/skill-management.ts +200 -52
- package/src/extensibility/skills.ts +29 -9
- package/src/gjc-runtime/empty-delete-gc.ts +94 -4
- package/src/gjc-runtime/gc-runtime.ts +29 -0
- package/src/gjc-runtime/linux-proc.ts +14 -0
- package/src/gjc-runtime/session-state-lock.ts +51 -8
- package/src/gjc-runtime/session-state-sidecar.ts +27 -11
- package/src/gjc-runtime/state-writer.ts +30 -29
- package/src/gjc-runtime/ultragoal-runtime.ts +189 -47
- package/src/gjc-runtime/ultragoal-succession.ts +1667 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +52 -1
- package/src/gjc-runtime/workflow-manifest.ts +16 -0
- package/src/harness-control-plane/session-lease.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +6 -6
- package/src/lsp/config.ts +10 -3
- package/src/lsp/index.ts +1 -1
- package/src/lsp/path-trust.ts +69 -14
- package/src/main.ts +6 -1
- package/src/modes/acp/acp-agent.ts +16 -3
- package/src/modes/components/bash-execution.ts +4 -1
- package/src/modes/components/execution-shared.ts +4 -1
- package/src/modes/components/session-observer-overlay.ts +1 -1
- package/src/modes/components/tool-status-header.ts +3 -1
- package/src/modes/controllers/command-controller.ts +1 -0
- package/src/modes/controllers/event-controller.ts +3 -2
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +6 -3
- package/src/modes/interactive-mode.ts +6 -5
- package/src/modes/print-mode.ts +5 -1
- package/src/modes/prompt-action-autocomplete.ts +54 -27
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/types.ts +16 -3
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/tools/subagent.md +1 -0
- package/src/runtime-mcp/manager.ts +20 -2
- package/src/runtime-mcp/pool.test.ts +20 -11
- package/src/sdk/broker/broker.ts +119 -28
- package/src/sdk/broker/ensure.ts +248 -41
- package/src/sdk/broker/lifecycle-ledger.ts +7 -17
- package/src/sdk/broker/lifecycle.ts +56 -6
- package/src/sdk/broker/startup-failure.ts +15 -6
- package/src/sdk/bus/chat-daemon-control.ts +3 -1
- package/src/sdk/bus/chat-daemon-runtime.ts +35 -5
- package/src/sdk/bus/chat-effect-journal.ts +132 -7
- package/src/sdk/bus/conversation-store.ts +29 -0
- package/src/sdk/bus/lifecycle-commands.ts +17 -11
- package/src/sdk/bus/slack-conversation.ts +1 -0
- package/src/sdk/bus/slack-daemon.ts +387 -48
- package/src/sdk/bus/telegram-daemon.ts +16 -2
- package/src/sdk/bus/topic-registry.ts +19 -0
- package/src/sdk/cli/master-cli.ts +1 -1
- package/src/sdk/cli/session-cli.ts +2 -2
- package/src/sdk/client/client.ts +66 -17
- package/src/sdk/host/host.ts +29 -9
- package/src/sdk/host/query/revision-store.ts +19 -13
- package/src/sdk/host/reverse-leases.ts +7 -4
- package/src/sdk/host/session-runtime.test.ts +50 -8
- package/src/sdk/host/session-runtime.ts +158 -51
- package/src/sdk/lifecycle/service.ts +4 -0
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/router/session-router.ts +89 -38
- package/src/sdk/session.ts +155 -26
- package/src/sdk/transport/relay.ts +35 -13
- package/src/session/agent-session.ts +117 -52
- package/src/session/fallback-chain-controller.ts +38 -0
- package/src/session/internal/managed-session-storage.ts +19 -5
- package/src/session/messages.ts +5 -0
- package/src/session/session-manager.ts +29 -5
- package/src/session-import/redact.ts +5 -1
- package/src/setup/paseo/announce.ts +49 -43
- package/src/setup/paseo/setup-deps.ts +1 -1
- package/src/skill-state/canonical-skills.ts +12 -0
- package/src/skills/skills.test.ts +41 -21
- package/src/system-prompt.ts +25 -3
- package/src/task/executor.ts +5 -0
- package/src/task/index.ts +4 -0
- package/src/tools/bash.ts +28 -11
- package/src/tools/gh.ts +26 -3
- package/src/tools/github-cache.ts +68 -16
- package/src/tools/index.ts +2 -0
- package/src/tools/job.ts +13 -2
- package/src/tools/read.ts +14 -2
- package/src/tools/render-utils.ts +1 -3
- package/src/tools/resource-gc.ts +28 -8
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +15 -0
- package/src/tools/subagent.ts +3 -2
- package/src/tools/tool-catalog.generated.ts +2 -2
- package/src/tools/ultragoal-ask-guard.ts +2 -2
|
@@ -33,8 +33,6 @@ import { AgentDirSessionLifecycleClient } from "../../sdk/lifecycle/broker-clien
|
|
|
33
33
|
import { sessionListPageFromResponse, traverseSessionList } from "../../sdk/session-list";
|
|
34
34
|
import { PROVIDER_EXTENDS, PROVIDER_KEY, resolvePaseoHome } from "./setup-deps";
|
|
35
35
|
|
|
36
|
-
export { resolvePaseoHome } from "./setup-deps";
|
|
37
|
-
|
|
38
36
|
/** Cheap liveness probe of the daemon socket. Only gates the expensive CLI spawn. */
|
|
39
37
|
const DAEMON_PROBE_TIMEOUT_MS = 750;
|
|
40
38
|
/** Bound on the broker `session.list` traversal that proves our own liveness. */
|
|
@@ -87,10 +85,17 @@ export interface PaseoAnnounceDependencies {
|
|
|
87
85
|
readonly providerKey: string;
|
|
88
86
|
readonly cwd: string;
|
|
89
87
|
readonly sessionId: string;
|
|
88
|
+
readonly daemonTarget: PaseoDaemonTarget;
|
|
89
|
+
readonly daemonHost: string;
|
|
90
90
|
readonly signal?: AbortSignal;
|
|
91
91
|
}): Promise<PaseoAnnounceOutcome>;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
interface ResolvedDaemonSelection {
|
|
95
|
+
readonly target: PaseoDaemonTarget;
|
|
96
|
+
readonly host: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
95
100
|
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
|
|
96
101
|
}
|
|
@@ -161,43 +166,39 @@ export function parseDaemonListen(raw: string): PaseoDaemonTarget | undefined {
|
|
|
161
166
|
export async function resolveDaemonTarget(
|
|
162
167
|
config: unknown,
|
|
163
168
|
deps: Pick<PaseoAnnounceDependencies, "env" | "paseoHome" | "readJson">,
|
|
164
|
-
): Promise<PaseoDaemonTarget> {
|
|
165
|
-
return (await
|
|
169
|
+
): Promise<PaseoDaemonTarget | undefined> {
|
|
170
|
+
return (await resolveDaemonSelection(config, deps))?.target;
|
|
166
171
|
}
|
|
167
172
|
|
|
168
|
-
async function
|
|
173
|
+
async function resolveDaemonSelection(
|
|
169
174
|
config: unknown,
|
|
170
175
|
deps: Pick<PaseoAnnounceDependencies, "env" | "paseoHome" | "readJson">,
|
|
171
|
-
): Promise<
|
|
176
|
+
): Promise<ResolvedDaemonSelection | undefined> {
|
|
172
177
|
const explicitHost = deps.env.PASEO_HOST?.trim();
|
|
173
178
|
if (explicitHost) {
|
|
174
|
-
const
|
|
175
|
-
return
|
|
179
|
+
const target = parseDaemonListen(explicitHost);
|
|
180
|
+
return target ? { target, host: explicitHost } : undefined;
|
|
181
|
+
}
|
|
182
|
+
const explicitListen = deps.env.PASEO_LISTEN?.trim();
|
|
183
|
+
if (explicitListen) {
|
|
184
|
+
const target = parseDaemonListen(explicitListen);
|
|
185
|
+
return target ? { target, host: explicitListen } : undefined;
|
|
176
186
|
}
|
|
177
187
|
|
|
178
188
|
const pidFile = asRecord(await deps.readJson(path.join(deps.paseoHome, "paseo.pid")));
|
|
179
189
|
const pidListen = typeof pidFile?.listen === "string" ? pidFile.listen : pidFile?.sockPath;
|
|
180
190
|
const pidTarget = typeof pidListen === "string" ? parseDaemonListen(pidListen) : undefined;
|
|
191
|
+
if (pidTarget?.kind === "ipc") return { target: pidTarget, host: pidListen as string };
|
|
181
192
|
|
|
182
193
|
const parsed = asRecord(config);
|
|
183
194
|
const configured = asRecord(parsed?.daemon)?.listen ?? parsed?.listen;
|
|
184
|
-
|
|
185
|
-
const configuredTarget =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
: undefined;
|
|
192
|
-
const directIpc = deps.env.PASEO_LISTEN?.trim();
|
|
193
|
-
const directIpcTarget = directIpc ? parseDaemonListen(directIpc) : undefined;
|
|
194
|
-
const targets = [
|
|
195
|
-
directIpcTarget?.kind === "ipc" ? directIpcTarget : undefined,
|
|
196
|
-
pidIpc,
|
|
197
|
-
configuredIpc,
|
|
198
|
-
configuredTcp,
|
|
199
|
-
].filter((target): target is PaseoDaemonTarget => target !== undefined);
|
|
200
|
-
return targets.length > 0 ? [targets[0]!] : [DEFAULT_DAEMON_TARGET];
|
|
195
|
+
if (typeof configured !== "string") return { target: DEFAULT_DAEMON_TARGET, host: "localhost:6767" };
|
|
196
|
+
const configuredTarget = parseDaemonListen(configured);
|
|
197
|
+
if (!configuredTarget) return { target: DEFAULT_DAEMON_TARGET, host: "localhost:6767" };
|
|
198
|
+
if (configured.trim() === "127.0.0.1:6767") {
|
|
199
|
+
return { target: DEFAULT_DAEMON_TARGET, host: "localhost:6767" };
|
|
200
|
+
}
|
|
201
|
+
return { target: configuredTarget, host: configured };
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
/**
|
|
@@ -243,24 +244,26 @@ export async function announceSessionToPaseo(
|
|
|
243
244
|
const cli = deps.resolveCli();
|
|
244
245
|
if (!cli) return { kind: "skipped", reason: "cli-missing" };
|
|
245
246
|
|
|
246
|
-
const
|
|
247
|
-
if (deps.env.PASEO_HOST?.trim()
|
|
247
|
+
const selection = await resolveDaemonSelection(config, deps);
|
|
248
|
+
if ((deps.env.PASEO_HOST?.trim() || deps.env.PASEO_LISTEN?.trim()) && selection === undefined)
|
|
248
249
|
return { kind: "skipped", reason: "unsupported-daemon-target" };
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (await deps.probeDaemon(target)) {
|
|
252
|
-
reachable = true;
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
if (!reachable) return { kind: "skipped", reason: "daemon-unreachable" };
|
|
250
|
+
if (!selection || !(await deps.probeDaemon(selection.target)))
|
|
251
|
+
return { kind: "skipped", reason: "daemon-unreachable" };
|
|
257
252
|
if (signal?.aborted) return { kind: "failed", detail: "Paseo announcement cancelled" };
|
|
258
253
|
|
|
259
254
|
// Importing a session the broker does not yet report as live would make ACP
|
|
260
255
|
// `session/load` resume a copy instead of attaching to the running host.
|
|
261
256
|
if (!(await deps.isSessionLive(input.sessionId, input.cwd))) return { kind: "skipped", reason: "session-not-live" };
|
|
262
257
|
|
|
263
|
-
return await deps.runImport({
|
|
258
|
+
return await deps.runImport({
|
|
259
|
+
cli,
|
|
260
|
+
providerKey,
|
|
261
|
+
cwd: input.cwd,
|
|
262
|
+
sessionId: input.sessionId,
|
|
263
|
+
daemonTarget: selection.target,
|
|
264
|
+
daemonHost: selection.host,
|
|
265
|
+
signal,
|
|
266
|
+
});
|
|
264
267
|
}
|
|
265
268
|
|
|
266
269
|
/** Only a daemon that is not up yet, or a registration that has not landed yet, is worth retrying. */
|
|
@@ -369,17 +372,13 @@ function isSessionLive(agentDir: string): (sessionId: string, cwd: string) => Pr
|
|
|
369
372
|
response => sessionListPageFromResponse(response),
|
|
370
373
|
);
|
|
371
374
|
for (const { sessions } of pages) {
|
|
372
|
-
if (
|
|
375
|
+
if (sessionListContainsLiveSession(sessions, sessionId, canonicalCwd)) return true;
|
|
373
376
|
}
|
|
374
377
|
return false;
|
|
375
378
|
};
|
|
376
379
|
}
|
|
377
380
|
|
|
378
|
-
export function
|
|
379
|
-
sessions: readonly unknown[],
|
|
380
|
-
sessionId: string,
|
|
381
|
-
cwd: string,
|
|
382
|
-
): boolean {
|
|
381
|
+
export function sessionListContainsLiveSession(sessions: readonly unknown[], sessionId: string, cwd: string): boolean {
|
|
383
382
|
for (const value of sessions) {
|
|
384
383
|
const session = asRecord(value);
|
|
385
384
|
if (session?.sessionId !== sessionId || session.live !== true) continue;
|
|
@@ -403,6 +402,8 @@ function runImport(env: NodeJS.ProcessEnv) {
|
|
|
403
402
|
readonly providerKey: string;
|
|
404
403
|
readonly cwd: string;
|
|
405
404
|
readonly sessionId: string;
|
|
405
|
+
readonly daemonTarget: PaseoDaemonTarget;
|
|
406
|
+
readonly daemonHost: string;
|
|
406
407
|
readonly signal?: AbortSignal;
|
|
407
408
|
}): Promise<PaseoAnnounceOutcome> => {
|
|
408
409
|
const timeoutController = new AbortController();
|
|
@@ -413,7 +414,12 @@ function runImport(env: NodeJS.ProcessEnv) {
|
|
|
413
414
|
try {
|
|
414
415
|
const child = Bun.spawn(
|
|
415
416
|
[input.cli, "import", "--provider", input.providerKey, "--cwd", input.cwd, input.sessionId],
|
|
416
|
-
{
|
|
417
|
+
{
|
|
418
|
+
stdout: "ignore",
|
|
419
|
+
stderr: "pipe",
|
|
420
|
+
signal,
|
|
421
|
+
env: { ...env, PASEO_HOST: input.daemonHost },
|
|
422
|
+
},
|
|
417
423
|
);
|
|
418
424
|
const [exitCode, stderr] = await Promise.all([child.exited, new Response(child.stderr).text()]);
|
|
419
425
|
if (input.signal?.aborted) return { kind: "failed", detail: "Paseo announcement cancelled" };
|
|
@@ -87,7 +87,7 @@ export function resolvePaseoHome(env: NodeJS.ProcessEnv = process.env, home: str
|
|
|
87
87
|
export function createDefaultPaseoPaths(
|
|
88
88
|
agentDir: string = getAgentDir(),
|
|
89
89
|
home: string = os.homedir(),
|
|
90
|
-
env: NodeJS.ProcessEnv =
|
|
90
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
91
91
|
): PaseoPaths {
|
|
92
92
|
const paseoHome = resolvePaseoHome(env, home);
|
|
93
93
|
return {
|
|
@@ -2,3 +2,15 @@
|
|
|
2
2
|
export const CANONICAL_GJC_WORKFLOW_SKILLS = ["deep-interview", "ralplan", "ultragoal", "autoresearch"] as const;
|
|
3
3
|
|
|
4
4
|
export type CanonicalGjcWorkflowSkill = (typeof CANONICAL_GJC_WORKFLOW_SKILLS)[number];
|
|
5
|
+
|
|
6
|
+
const CANONICAL_GJC_WORKFLOW_SKILL_SET = new Set<string>(CANONICAL_GJC_WORKFLOW_SKILLS);
|
|
7
|
+
|
|
8
|
+
/** Case-insensitive, Win32-path-equivalent key used for skill collision checks. */
|
|
9
|
+
export function getSkillFilesystemIdentity(name: string): string {
|
|
10
|
+
return name.replace(/[. ]+$/u, "").toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Whether a name resolves to a bundled workflow under portable filesystem semantics. */
|
|
14
|
+
export function isCanonicalGjcWorkflowSkillFilesystemAlias(name: string): boolean {
|
|
15
|
+
return CANONICAL_GJC_WORKFLOW_SKILL_SET.has(getSkillFilesystemIdentity(name));
|
|
16
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
1
|
+
import { describe, expect, test, vi } from "bun:test";
|
|
2
|
+
import * as nodeFs from "node:fs";
|
|
2
3
|
import * as fs from "node:fs/promises";
|
|
3
4
|
import * as path from "node:path";
|
|
5
|
+
import type { LoadContext } from "../capability/types";
|
|
4
6
|
import { getEmbeddedDefaultGjcSkills } from "../defaults/gjc-defaults";
|
|
5
7
|
import { buildSkillPromptMessage } from "../extensibility/skills";
|
|
6
8
|
import { SKILL_FRONTMATTER_SCAN_BYTES, SKILL_FRONTMATTER_SCAN_TOTAL_BYTES, scanSkillDescriptorsFromDir } from "./index";
|
|
7
9
|
|
|
8
|
-
function makeContext():
|
|
9
|
-
return { cwd:
|
|
10
|
+
function makeContext(root: string): LoadContext {
|
|
11
|
+
return { cwd: root, home: root, repoRoot: null };
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
describe("skill descriptors", () => {
|
|
@@ -22,22 +24,31 @@ describe("skill descriptors", () => {
|
|
|
22
24
|
`---\nname: bounded\ndescription: bounded scan\n---\n${body}`,
|
|
23
25
|
);
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
// Discovery reads the validated descriptor rather than reopening with Bun.file.
|
|
28
|
+
// Observe only the handles this scan opens: a FileHandle prototype spy is
|
|
29
|
+
// process-wide, so unrelated reads from concurrently running tests
|
|
30
|
+
// interleave and make global counts flaky.
|
|
31
|
+
const reads: Array<unknown[]> = [];
|
|
32
|
+
const probe = await fs.open(path.join(skillDir, "SKILL.md"), "r");
|
|
33
|
+
const fileHandlePrototype = Object.getPrototypeOf(probe) as fs.FileHandle;
|
|
34
|
+
await probe.close();
|
|
35
|
+
const originalOpen = nodeFs.promises.open;
|
|
36
|
+
type OpenArgs = Parameters<typeof nodeFs.promises.open>;
|
|
37
|
+
const openSpy = vi.spyOn(nodeFs.promises, "open").mockImplementation((async (...args: OpenArgs) => {
|
|
38
|
+
const handle = await originalOpen(...args);
|
|
39
|
+
const skillPath = args[0];
|
|
40
|
+
if (typeof skillPath === "string" && skillPath.endsWith("SKILL.md")) {
|
|
41
|
+
const originalRead = handle.read.bind(handle);
|
|
42
|
+
handle.read = (async (...readArgs: never[]) => {
|
|
43
|
+
reads.push(readArgs as unknown[]);
|
|
44
|
+
return originalRead(...readArgs);
|
|
45
|
+
}) as typeof handle.read;
|
|
46
|
+
}
|
|
47
|
+
return handle;
|
|
48
|
+
}) as typeof nodeFs.promises.open);
|
|
49
|
+
const readFileSpy = vi.spyOn(fileHandlePrototype, "readFile");
|
|
39
50
|
try {
|
|
40
|
-
const result = await scanSkillDescriptorsFromDir(makeContext(), {
|
|
51
|
+
const result = await scanSkillDescriptorsFromDir(makeContext(root), {
|
|
41
52
|
dir: root,
|
|
42
53
|
providerId: "test",
|
|
43
54
|
level: "project",
|
|
@@ -45,9 +56,18 @@ describe("skill descriptors", () => {
|
|
|
45
56
|
expect(result.items).toHaveLength(1);
|
|
46
57
|
expect(Object.hasOwn(result.items[0]?.metadata ?? {}, "content")).toBe(false);
|
|
47
58
|
expect(JSON.stringify(result.items[0]?.metadata)).not.toContain(bodyMarker);
|
|
48
|
-
|
|
59
|
+
const boundedReads = reads.filter(
|
|
60
|
+
call => call[1] === 0 && call[2] === SKILL_FRONTMATTER_SCAN_BYTES && call[3] === 0,
|
|
61
|
+
);
|
|
62
|
+
expect(boundedReads).toHaveLength(1);
|
|
63
|
+
const oversizedReads = reads.filter(
|
|
64
|
+
call => typeof call[2] === "number" && call[2] > SKILL_FRONTMATTER_SCAN_BYTES,
|
|
65
|
+
);
|
|
66
|
+
expect(oversizedReads).toHaveLength(0);
|
|
67
|
+
expect(readFileSpy).not.toHaveBeenCalled();
|
|
49
68
|
} finally {
|
|
50
|
-
(
|
|
69
|
+
openSpy.mockRestore();
|
|
70
|
+
readFileSpy.mockRestore();
|
|
51
71
|
}
|
|
52
72
|
} finally {
|
|
53
73
|
await fs.rm(root, { recursive: true, force: true });
|
|
@@ -63,7 +83,7 @@ describe("skill descriptors", () => {
|
|
|
63
83
|
path.join(skillDir, "SKILL.md"),
|
|
64
84
|
`---\nname: unterminated\ndescription: no closing delimiter\n${"x".repeat(SKILL_FRONTMATTER_SCAN_TOTAL_BYTES * 32)}`,
|
|
65
85
|
);
|
|
66
|
-
const result = await scanSkillDescriptorsFromDir(makeContext(), {
|
|
86
|
+
const result = await scanSkillDescriptorsFromDir(makeContext(root), {
|
|
67
87
|
dir: root,
|
|
68
88
|
providerId: "test",
|
|
69
89
|
level: "project",
|
package/src/system-prompt.ts
CHANGED
|
@@ -235,6 +235,10 @@ export async function resolvePromptInput(input: string | undefined, description:
|
|
|
235
235
|
export interface LoadContextFilesOptions {
|
|
236
236
|
/** Working directory to start walking up from. Default: getProjectDir() */
|
|
237
237
|
cwd?: string;
|
|
238
|
+
/** Agent directory backing native user-scope context and system prompt files. */
|
|
239
|
+
agentDir?: string;
|
|
240
|
+
/** Resolver-owned classification for `agentDir`; preserve it across HOME/config refreshes. */
|
|
241
|
+
profileAuthority?: "default" | "custom";
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
function dedupeExactContextFiles(
|
|
@@ -265,7 +269,11 @@ export async function loadProjectContextFilesResult(
|
|
|
265
269
|
options: LoadContextFilesOptions = {},
|
|
266
270
|
): Promise<ProjectContextFilesResult> {
|
|
267
271
|
const resolvedCwd = options.cwd ?? getProjectDir();
|
|
268
|
-
const result = await loadCapability(contextFileCapability.id, {
|
|
272
|
+
const result = await loadCapability(contextFileCapability.id, {
|
|
273
|
+
cwd: resolvedCwd,
|
|
274
|
+
agentDir: options.agentDir,
|
|
275
|
+
profileAuthority: options.profileAuthority,
|
|
276
|
+
});
|
|
269
277
|
const items = result.items as ContextFile[];
|
|
270
278
|
|
|
271
279
|
// Native user-global context applies everywhere and is least specific, so it
|
|
@@ -311,7 +319,11 @@ export async function loadProjectContextFiles(
|
|
|
311
319
|
export async function loadSystemPromptFiles(options: LoadContextFilesOptions = {}): Promise<string | null> {
|
|
312
320
|
const resolvedCwd = options.cwd ?? getProjectDir();
|
|
313
321
|
|
|
314
|
-
const result = await loadCapability<SystemPromptFile>(systemPromptCapability.id, {
|
|
322
|
+
const result = await loadCapability<SystemPromptFile>(systemPromptCapability.id, {
|
|
323
|
+
cwd: resolvedCwd,
|
|
324
|
+
agentDir: options.agentDir,
|
|
325
|
+
profileAuthority: options.profileAuthority,
|
|
326
|
+
});
|
|
315
327
|
|
|
316
328
|
if (result.items.length === 0) return null;
|
|
317
329
|
|
|
@@ -374,6 +386,10 @@ export interface BuildSystemPromptOptions {
|
|
|
374
386
|
skillsSettings?: SkillsSettings;
|
|
375
387
|
/** Working directory. Default: getProjectDir() */
|
|
376
388
|
cwd?: string;
|
|
389
|
+
/** Agent directory backing native user-scope prompt discovery. */
|
|
390
|
+
agentDir?: string;
|
|
391
|
+
/** Resolver-owned classification for `agentDir`; preserve it across HOME/config refreshes. */
|
|
392
|
+
profileAuthority?: "default" | "custom";
|
|
377
393
|
/** Pre-loaded context files (skips discovery if provided). */
|
|
378
394
|
contextFiles?: Array<{ path: string; content: string; depth?: number }>;
|
|
379
395
|
/** Skills provided directly to system prompt construction. */
|
|
@@ -603,10 +619,16 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
603
619
|
|
|
604
620
|
const systemPromptCustomizationPromise = logger.time("loadSystemPromptFiles", loadSystemPromptFiles, {
|
|
605
621
|
cwd: resolvedCwd,
|
|
622
|
+
agentDir: options.agentDir,
|
|
623
|
+
profileAuthority: options.profileAuthority,
|
|
606
624
|
});
|
|
607
625
|
const contextFilesPromise = providedContextFiles
|
|
608
626
|
? Promise.resolve({ contextFiles: providedContextFiles, warnings: [] })
|
|
609
|
-
: logger.time("loadProjectContextFiles", loadProjectContextFilesResult, {
|
|
627
|
+
: logger.time("loadProjectContextFiles", loadProjectContextFilesResult, {
|
|
628
|
+
cwd: resolvedCwd,
|
|
629
|
+
agentDir: options.agentDir,
|
|
630
|
+
profileAuthority: options.profileAuthority,
|
|
631
|
+
});
|
|
610
632
|
const workspaceTreePromise =
|
|
611
633
|
providedWorkspaceTree !== undefined
|
|
612
634
|
? Promise.resolve(providedWorkspaceTree)
|
package/src/task/executor.ts
CHANGED
|
@@ -281,6 +281,9 @@ export interface ExecutorOptions {
|
|
|
281
281
|
authStorage?: AuthStorage;
|
|
282
282
|
modelRegistry?: ModelRegistry;
|
|
283
283
|
settings?: Settings;
|
|
284
|
+
/** Parent session's selected agent directory and immutable resolver-owned authority. */
|
|
285
|
+
agentDir?: string;
|
|
286
|
+
profileAuthority?: "default" | "custom";
|
|
284
287
|
/** Parent session's registry; shared by child sessions for IRC routing and roster visibility. */
|
|
285
288
|
agentRegistry?: AgentRegistry;
|
|
286
289
|
/**
|
|
@@ -1952,6 +1955,8 @@ export async function runSubprocessOnce(options: ExecutorOptions): Promise<Singl
|
|
|
1952
1955
|
const { session } = await awaitAbortable(
|
|
1953
1956
|
canonicalSdk.createAgentSession({
|
|
1954
1957
|
cwd: worktree ?? cwd,
|
|
1958
|
+
agentDir: options.agentDir,
|
|
1959
|
+
profileAuthority: options.profileAuthority,
|
|
1955
1960
|
authStorage,
|
|
1956
1961
|
modelRegistry,
|
|
1957
1962
|
settings: subagentSettings,
|
package/src/task/index.ts
CHANGED
|
@@ -2337,6 +2337,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2337
2337
|
modelRegistry: this.session.modelRegistry,
|
|
2338
2338
|
agentRegistry: this.session.agentRegistry,
|
|
2339
2339
|
settings: this.session.settings,
|
|
2340
|
+
agentDir: this.session.getSessionAgentDir?.() ?? this.session.settings.getAgentDir(),
|
|
2341
|
+
profileAuthority: this.session.profileAuthority,
|
|
2340
2342
|
inheritedServiceTier: this.session.serviceTier,
|
|
2341
2343
|
contextFiles,
|
|
2342
2344
|
skills: availableSkills,
|
|
@@ -2421,6 +2423,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2421
2423
|
modelRegistry: this.session.modelRegistry,
|
|
2422
2424
|
agentRegistry: this.session.agentRegistry,
|
|
2423
2425
|
settings: this.session.settings,
|
|
2426
|
+
agentDir: this.session.getSessionAgentDir?.() ?? this.session.settings.getAgentDir(),
|
|
2427
|
+
profileAuthority: this.session.profileAuthority,
|
|
2424
2428
|
inheritedServiceTier: this.session.serviceTier,
|
|
2425
2429
|
contextFiles,
|
|
2426
2430
|
skills: availableSkills,
|
package/src/tools/bash.ts
CHANGED
|
@@ -103,6 +103,7 @@ const BASH_ERROR_MAX_BYTES = 4096;
|
|
|
103
103
|
const ARTIFACT_SAVE_DIAGNOSTIC_MAX_BYTES = 256;
|
|
104
104
|
const BASH_ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
105
105
|
const MASTER_CAPABILITY_ENV = "GJC_MASTER_CAPABILITY";
|
|
106
|
+
const MASTER_OWNER_SESSION_ENV = "GJC_MASTER_OWNER_SESSION_ID";
|
|
106
107
|
const DEFAULT_AUTO_BACKGROUND_THRESHOLD_MS = 60_000;
|
|
107
108
|
const ACP_RELEASE_TIMEOUT_MS = 1_000;
|
|
108
109
|
const READ_ONLY_BASH_ENV: Record<string, string> = {
|
|
@@ -186,8 +187,9 @@ function artifactReferenceIsReachable(text: string, result: BashResult | BashInt
|
|
|
186
187
|
);
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
function suffixPrefixOverlap(source: string, target: string): number {
|
|
190
|
+
export function suffixPrefixOverlap(source: string, target: string): number {
|
|
190
191
|
if (source.length === 0 || target.length === 0) return 0;
|
|
192
|
+
if (target.startsWith(source)) return source.length;
|
|
191
193
|
const prefix = new Uint32Array(target.length);
|
|
192
194
|
for (let index = 1; index < target.length; index += 1) {
|
|
193
195
|
let length = prefix[index - 1] ?? 0;
|
|
@@ -244,10 +246,17 @@ function failureStatusCause(
|
|
|
244
246
|
if (result.cancelled) return leadingStatus ?? lastStatus ?? "Command cancelled";
|
|
245
247
|
if (isInteractiveResult(result) && result.timedOut) return lastStatus ?? "Command timed out";
|
|
246
248
|
if (result.exitCode === undefined) return "Command failed: missing exit status";
|
|
247
|
-
if (result.exitCode !== 0) return
|
|
249
|
+
if (result.exitCode !== 0) return nonZeroExitCause(result);
|
|
248
250
|
return undefined;
|
|
249
251
|
}
|
|
250
252
|
|
|
253
|
+
function nonZeroExitCause(result: BashResult | BashInteractiveResult): string {
|
|
254
|
+
if ("signal" in result && result.signal) {
|
|
255
|
+
return `Command terminated by ${result.signal} (exit code ${result.exitCode})`;
|
|
256
|
+
}
|
|
257
|
+
return `Command exited with code ${result.exitCode}`;
|
|
258
|
+
}
|
|
259
|
+
|
|
251
260
|
function removeTrailingFailureCause(text: string, cause: string | undefined): string {
|
|
252
261
|
if (!cause) return text;
|
|
253
262
|
for (const suffix of [`\n\n${cause}`, `\n${cause}`, cause]) {
|
|
@@ -717,7 +726,12 @@ export function masterCommandEnvOverrides(
|
|
|
717
726
|
directMasterSpawn: boolean,
|
|
718
727
|
): Record<string, string> {
|
|
719
728
|
if (directMasterSpawn) return {};
|
|
720
|
-
|
|
729
|
+
// The master capability is single-use spawn authority and the owner id is
|
|
730
|
+
// spawn-lineage identity: neither is ambient state for ordinary Bash or a
|
|
731
|
+
// chained/pipelined descendant (issue #5374).
|
|
732
|
+
return Object.fromEntries(
|
|
733
|
+
Object.entries(env ?? {}).filter(([key]) => key !== MASTER_CAPABILITY_ENV && key !== MASTER_OWNER_SESSION_ENV),
|
|
734
|
+
);
|
|
721
735
|
}
|
|
722
736
|
|
|
723
737
|
function escapeBashEnvValueForDisplay(value: string): string {
|
|
@@ -896,9 +910,8 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
896
910
|
throw new ToolError(formatBashFailureMessage(result, `${outputText}\n\nCommand failed: missing exit status`));
|
|
897
911
|
}
|
|
898
912
|
if (result.exitCode !== 0) {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
);
|
|
913
|
+
const cause = nonZeroExitCause(result);
|
|
914
|
+
throw new ToolError(formatBashFailureMessage(result, `${outputText}\n\n${cause}`));
|
|
902
915
|
}
|
|
903
916
|
return outputText;
|
|
904
917
|
}
|
|
@@ -1438,7 +1451,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1438
1451
|
// command that contained shell syntax into a privileged one.
|
|
1439
1452
|
const directMasterSpawn = isStrictDirectSdkSpawnCommand(rawCommand) && isStrictDirectSdkSpawnCommand(command);
|
|
1440
1453
|
const masterCapability = directMasterSpawn ? this.session.getMasterBashCapability?.() : undefined;
|
|
1441
|
-
|
|
1454
|
+
// Master ownership is lineage identity, not session identity: it travels
|
|
1455
|
+
// under its own variable on every bash env so a master-owned child can
|
|
1456
|
+
// still read its OWN id from GJC_SESSION_ID (issue #5374).
|
|
1457
|
+
const masterOwnerSessionId = this.session.getMasterOwnerSessionId?.();
|
|
1442
1458
|
const commandEnvOverrides = masterCommandEnvOverrides(expandedEnv, directMasterSpawn);
|
|
1443
1459
|
const resolvedEnv = {
|
|
1444
1460
|
...buildGjcRuntimeSessionEnv({
|
|
@@ -1451,7 +1467,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1451
1467
|
: {}),
|
|
1452
1468
|
...commandEnvOverrides,
|
|
1453
1469
|
...(masterCapability ? { [MASTER_CAPABILITY_ENV]: masterCapability } : {}),
|
|
1454
|
-
...(masterOwnerSessionId ? {
|
|
1470
|
+
...(masterOwnerSessionId ? { [MASTER_OWNER_SESSION_ENV]: masterOwnerSessionId } : {}),
|
|
1455
1471
|
...(this.session.bashRestrictionProfile === "read-only" ? READ_ONLY_BASH_ENV : {}),
|
|
1456
1472
|
...(allowedPrefixes && allowedPrefixes.length > 0 ? { [GJC_RESTRICTED_ROLE_AGENT_BASH_ENV]: "1" } : {}),
|
|
1457
1473
|
};
|
|
@@ -1503,8 +1519,9 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1503
1519
|
resolvedEnv: Record<string, string> | undefined,
|
|
1504
1520
|
): Promise<BashResult> {
|
|
1505
1521
|
const masterCapability = resolvedEnv?.[MASTER_CAPABILITY_ENV];
|
|
1522
|
+
const ownerSessionId = resolvedEnv?.[MASTER_OWNER_SESSION_ENV];
|
|
1506
1523
|
const sessionAgentDir = resolvedEnv?.GJC_CODING_AGENT_DIR;
|
|
1507
|
-
if (masterCapability === undefined || sessionAgentDir === undefined)
|
|
1524
|
+
if (masterCapability === undefined || ownerSessionId === undefined || sessionAgentDir === undefined)
|
|
1508
1525
|
throw new ToolError("Master spawn authority context is unavailable.");
|
|
1509
1526
|
const args = parseDirectSdkSpawnArgs(command);
|
|
1510
1527
|
args.agentDir = sessionAgentDir;
|
|
@@ -1512,8 +1529,8 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1512
1529
|
const request = runSdkSpawn(args, {
|
|
1513
1530
|
env: {
|
|
1514
1531
|
...process.env,
|
|
1515
|
-
|
|
1516
|
-
|
|
1532
|
+
[MASTER_CAPABILITY_ENV]: masterCapability,
|
|
1533
|
+
[MASTER_OWNER_SESSION_ENV]: ownerSessionId,
|
|
1517
1534
|
GJC_CODING_AGENT_DIR: sessionAgentDir,
|
|
1518
1535
|
},
|
|
1519
1536
|
});
|
package/src/tools/gh.ts
CHANGED
|
@@ -2738,7 +2738,14 @@ export async function getOrFetchIssue(options: IssueViewLookupOptions): Promise<
|
|
|
2738
2738
|
}
|
|
2739
2739
|
}
|
|
2740
2740
|
|
|
2741
|
-
const doFetch = () =>
|
|
2741
|
+
const doFetch = (signal?: AbortSignal) =>
|
|
2742
|
+
fetchIssueViewFresh(
|
|
2743
|
+
options.cwd,
|
|
2744
|
+
repo,
|
|
2745
|
+
identifier,
|
|
2746
|
+
includeComments,
|
|
2747
|
+
signal && options.signal ? AbortSignal.any([signal, options.signal]) : (signal ?? options.signal),
|
|
2748
|
+
);
|
|
2742
2749
|
|
|
2743
2750
|
if (!repo || cacheNumber === undefined) {
|
|
2744
2751
|
const fresh = await doFetch();
|
|
@@ -2752,6 +2759,7 @@ export async function getOrFetchIssue(options: IssueViewLookupOptions): Promise<
|
|
|
2752
2759
|
includeComments,
|
|
2753
2760
|
settings: options.settings,
|
|
2754
2761
|
authKey,
|
|
2762
|
+
signal: options.signal,
|
|
2755
2763
|
fetchFresh: doFetch,
|
|
2756
2764
|
});
|
|
2757
2765
|
return {
|
|
@@ -2771,7 +2779,14 @@ export async function getOrFetchIssue(options: IssueViewLookupOptions): Promise<
|
|
|
2771
2779
|
export async function getOrFetchPr(options: PrViewLookupOptions): Promise<ViewLookupResult<GhPrViewData>> {
|
|
2772
2780
|
const includeComments = options.includeComments ?? true;
|
|
2773
2781
|
const authKey = options.cacheAuthKey === undefined ? (resolveGithubCacheAuthKey() ?? null) : options.cacheAuthKey;
|
|
2774
|
-
const doFetch = () =>
|
|
2782
|
+
const doFetch = (signal?: AbortSignal) =>
|
|
2783
|
+
fetchPrViewFresh(
|
|
2784
|
+
options.cwd,
|
|
2785
|
+
options.repo,
|
|
2786
|
+
options.number,
|
|
2787
|
+
includeComments,
|
|
2788
|
+
signal && options.signal ? AbortSignal.any([signal, options.signal]) : (signal ?? options.signal),
|
|
2789
|
+
);
|
|
2775
2790
|
const lookup = await getOrFetchView<GhPrViewData>({
|
|
2776
2791
|
repo: options.repo,
|
|
2777
2792
|
kind: "pr",
|
|
@@ -2779,6 +2794,7 @@ export async function getOrFetchPr(options: PrViewLookupOptions): Promise<ViewLo
|
|
|
2779
2794
|
includeComments,
|
|
2780
2795
|
settings: options.settings,
|
|
2781
2796
|
authKey,
|
|
2797
|
+
signal: options.signal,
|
|
2782
2798
|
fetchFresh: doFetch,
|
|
2783
2799
|
});
|
|
2784
2800
|
return {
|
|
@@ -3121,7 +3137,13 @@ async function fetchPrDiffFresh(
|
|
|
3121
3137
|
*/
|
|
3122
3138
|
export async function getOrFetchPrDiff(options: PrDiffLookupOptions): Promise<ViewLookupResult<PrDiffPayload>> {
|
|
3123
3139
|
const authKey = options.cacheAuthKey === undefined ? (resolveGithubCacheAuthKey() ?? null) : options.cacheAuthKey;
|
|
3124
|
-
const doFetch = () =>
|
|
3140
|
+
const doFetch = (signal?: AbortSignal) =>
|
|
3141
|
+
fetchPrDiffFresh(
|
|
3142
|
+
options.cwd,
|
|
3143
|
+
options.repo,
|
|
3144
|
+
options.number,
|
|
3145
|
+
signal && options.signal ? AbortSignal.any([signal, options.signal]) : (signal ?? options.signal),
|
|
3146
|
+
);
|
|
3125
3147
|
const lookup = await getOrFetchView<PrDiffPayload>({
|
|
3126
3148
|
repo: options.repo,
|
|
3127
3149
|
kind: "pr-diff",
|
|
@@ -3129,6 +3151,7 @@ export async function getOrFetchPrDiff(options: PrDiffLookupOptions): Promise<Vi
|
|
|
3129
3151
|
includeComments: false,
|
|
3130
3152
|
settings: options.settings,
|
|
3131
3153
|
authKey,
|
|
3154
|
+
signal: options.signal,
|
|
3132
3155
|
fetchFresh: doFetch,
|
|
3133
3156
|
});
|
|
3134
3157
|
return {
|