@gajae-code/coding-agent 0.15.5 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -1
- package/dist/types/async/job-manager.d.ts +83 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +17 -0
- package/dist/types/cli/model-presets-cli.d.ts +9 -0
- package/dist/types/cli/root-flags.d.ts +14 -0
- package/dist/types/cli/update-cli.d.ts +15 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/commands/auth-gateway.d.ts +3 -0
- package/dist/types/commands/gc.d.ts +13 -0
- package/dist/types/commands/launch.d.ts +10 -0
- package/dist/types/commands/model-presets.d.ts +21 -0
- package/dist/types/config/autorouting-tier-map.d.ts +120 -0
- package/dist/types/config/file-lock.d.ts +54 -2
- package/dist/types/config/internal/model-preset-registry-test-state.d.ts +4 -0
- package/dist/types/config/internal/model-preset-registry-test-support.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +3 -2
- package/dist/types/config/model-preset-registry.d.ts +368 -0
- package/dist/types/config/model-profile-activation.d.ts +39 -0
- package/dist/types/config/model-profiles.d.ts +5 -3
- package/dist/types/config/model-registry.d.ts +17 -5
- package/dist/types/config/settings-schema.d.ts +19 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -5
- package/dist/types/coordinator-mcp/projection-scan.d.ts +54 -0
- package/dist/types/crash/record-loader.d.ts +15 -0
- package/dist/types/extensibility/extensions/types.d.ts +2 -5
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +58 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +35 -6
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +72 -1
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/main.d.ts +3 -1
- package/dist/types/master-mode/context.d.ts +21 -0
- package/dist/types/master-mode/first-request.d.ts +34 -0
- package/dist/types/master-mode/snapshot.d.ts +32 -0
- package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
- package/dist/types/modes/components/jobs-overlay-model.d.ts +4 -2
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
- package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
- package/dist/types/modes/components/status-line/segments.d.ts +10 -0
- package/dist/types/modes/jobs-observer.d.ts +36 -0
- package/dist/types/modes/print-mode.d.ts +17 -0
- package/dist/types/modes/ui-language.d.ts +7 -1
- package/dist/types/sdk/broker/broker.d.ts +80 -1
- package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
- package/dist/types/sdk/broker/index.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +26 -0
- package/dist/types/sdk/broker/master-capability.d.ts +10 -0
- package/dist/types/sdk/broker/session-index.d.ts +40 -11
- package/dist/types/sdk/broker/session-scope.d.ts +72 -0
- package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
- package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
- package/dist/types/sdk/broker/state-version.d.ts +4 -1
- package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
- package/dist/types/sdk/bus/index.d.ts +6 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +3 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +4 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/master-cli.d.ts +63 -0
- package/dist/types/sdk/cli/rows.d.ts +4 -3
- package/dist/types/sdk/cli/session-cli.d.ts +18 -2
- package/dist/types/sdk/client/client.d.ts +3 -2
- package/dist/types/sdk/client/discovery.d.ts +1 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +2 -0
- package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
- package/dist/types/sdk/host/host.d.ts +10 -0
- package/dist/types/sdk/host/model-pin.d.ts +7 -2
- package/dist/types/sdk/host/sdk-run-capability.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +67 -1
- package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
- package/dist/types/sdk/lifecycle/service.d.ts +15 -3
- package/dist/types/sdk/lifecycle-session.d.ts +2 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +11 -1
- package/dist/types/sdk/protocol/adapter-validation.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +16 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -1
- package/dist/types/session/account-inventory.d.ts +1 -0
- package/dist/types/session/agent-session.d.ts +24 -7
- package/dist/types/session/credential-store-errors.d.ts +1 -0
- package/dist/types/session/fold-coordinator.d.ts +201 -0
- package/dist/types/session/sdk-run-capability-internal.d.ts +7 -0
- package/dist/types/session/sdk-run-capability.d.ts +2 -0
- package/dist/types/session/session-manager.d.ts +65 -0
- package/dist/types/session/yield-queue.d.ts +5 -4
- package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
- package/dist/types/telemetry/control.d.ts +11 -0
- package/dist/types/telemetry/events.d.ts +20 -0
- package/dist/types/telemetry/index.d.ts +3 -0
- package/dist/types/telemetry/transport.d.ts +22 -0
- package/dist/types/tools/bash-interactive.d.ts +23 -0
- package/dist/types/tools/bash.d.ts +4 -0
- package/dist/types/tools/gh.d.ts +8 -0
- package/dist/types/tools/index.d.ts +10 -5
- package/dist/types/web/search/providers/base.d.ts +9 -4
- package/dist/types/web/search/providers/xai.d.ts +2 -1
- package/dist/types/web/search/types.d.ts +14 -0
- package/package.json +10 -7
- package/scripts/generate-hotkeys-docs.ts +4 -0
- package/scripts/generate-sdk-operation-inventory.ts +9 -3
- package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
- package/src/async/job-manager.ts +671 -39
- package/src/cli/accounts-cli.ts +9 -5
- package/src/cli/args.ts +17 -0
- package/src/cli/auth-broker-cli.ts +40 -12
- package/src/cli/auth-gateway-cli.ts +273 -97
- package/src/cli/config-cli.ts +2 -2
- package/src/cli/fast-help.ts +4 -0
- package/src/cli/model-presets-cli.ts +100 -0
- package/src/cli/root-flags.ts +5 -0
- package/src/cli/update-cli.ts +117 -16
- package/src/cli.ts +6 -2
- package/src/commands/auth-gateway.ts +12 -8
- package/src/commands/gc.ts +19 -0
- package/src/commands/launch.ts +86 -18
- package/src/commands/model-presets.ts +24 -0
- package/src/commands/sdk.ts +113 -21
- package/src/config/autorouting-tier-map.ts +44 -0
- package/src/config/file-lock-gc.ts +9 -4
- package/src/config/file-lock.ts +890 -99
- package/src/config/internal/model-preset-registry-test-state.ts +28 -0
- package/src/config/internal/model-preset-registry-test-support.ts +11 -0
- package/src/config/keybindings.ts +6 -2
- package/src/config/model-preset-registry.ts +2795 -0
- package/src/config/model-profile-activation.ts +115 -28
- package/src/config/model-profile-contract.ts +4 -1
- package/src/config/model-profiles.ts +56 -14
- package/src/config/model-registry.ts +1044 -122
- package/src/config/model-resolver.ts +5 -0
- package/src/config/settings-schema.ts +19 -1
- package/src/config/settings.ts +11 -1
- package/src/coordinator-mcp/model-preset.ts +32 -17
- package/src/coordinator-mcp/projection-scan.ts +592 -0
- package/src/coordinator-mcp/question-state.ts +9 -11
- package/src/coordinator-mcp/server.ts +111 -42
- package/src/coordinator-mcp/session-reaper.ts +9 -3
- package/src/crash/index-store.ts +2 -0
- package/src/crash/record-loader.ts +10 -0
- package/src/extensibility/extensions/loader.ts +0 -1
- package/src/extensibility/extensions/types.ts +2 -3
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +1 -0
- package/src/gjc-runtime/empty-delete-gc.ts +301 -0
- package/src/gjc-runtime/gc-render.ts +29 -0
- package/src/gjc-runtime/gc-runtime.ts +174 -11
- package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
- package/src/gjc-runtime/session-state-lock.ts +1321 -147
- package/src/gjc-runtime/session-state-sidecar.ts +1004 -25
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -2
- package/src/gjc-runtime/tmux-sessions.ts +252 -13
- package/src/hooks/skill-state.ts +6 -0
- package/src/index.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/internal-urls/issue-pr-protocol.ts +4 -2
- package/src/lsp/config.ts +4 -3
- package/src/lsp/path-trust.ts +12 -6
- package/src/main.ts +66 -4
- package/src/master-mode/context.ts +50 -0
- package/src/master-mode/first-request.ts +65 -0
- package/src/master-mode/snapshot.ts +205 -0
- package/src/modes/acp/acp-agent.ts +616 -161
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
- package/src/modes/components/jobs-overlay-model.ts +99 -16
- package/src/modes/components/model-selector.ts +158 -18
- package/src/modes/components/oauth-selector.ts +6 -1
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/model-name.ts +53 -0
- package/src/modes/components/status-line/priority-row.ts +107 -0
- package/src/modes/components/status-line/segments.ts +29 -10
- package/src/modes/components/tool-status-header.ts +151 -15
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +51 -6
- package/src/modes/controllers/selector-controller.ts +191 -96
- package/src/modes/interactive-mode.ts +14 -1
- package/src/modes/jobs-observer.ts +149 -16
- package/src/modes/print-mode.ts +64 -0
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/ui-language.ts +49 -2
- package/src/modes/utils/injected-user-submission.ts +15 -1
- package/src/prompts/system/master-mode.md +28 -0
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/fold-receipt.md +3 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +17 -2
- package/src/sdk/broker/broker.ts +1671 -34
- package/src/sdk/broker/endpoint-authority.ts +86 -0
- package/src/sdk/broker/ensure.ts +5 -0
- package/src/sdk/broker/index.ts +3 -0
- package/src/sdk/broker/lifecycle.ts +217 -46
- package/src/sdk/broker/master-capability.ts +232 -0
- package/src/sdk/broker/session-index.ts +121 -17
- package/src/sdk/broker/session-scope.ts +272 -0
- package/src/sdk/broker/spawn-authority.ts +1031 -0
- package/src/sdk/broker/spawn-substrate.ts +548 -0
- package/src/sdk/broker/state-version.ts +26 -14
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/broker/worktree-occupancy.ts +90 -0
- package/src/sdk/bus/chat-daemon-control.ts +5 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +46 -5
- package/src/sdk/bus/index.ts +51 -4
- package/src/sdk/bus/kind-aware-reconciliation.ts +36 -6
- package/src/sdk/bus/prompt-reconciliation.ts +28 -4
- package/src/sdk/bus/slack-conversation.ts +5 -1
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/master-cli.ts +259 -0
- package/src/sdk/cli/rows.ts +11 -8
- package/src/sdk/cli/session-cli.ts +276 -6
- package/src/sdk/client/client.ts +14 -8
- package/src/sdk/client/discovery.ts +3 -1
- package/src/sdk/host/control/dispatch.ts +101 -37
- package/src/sdk/host/control/index.ts +2 -0
- package/src/sdk/host/control/operations.ts +2 -0
- package/src/sdk/host/control/runtime-gate.ts +203 -11
- package/src/sdk/host/host.ts +27 -2
- package/src/sdk/host/model-pin.ts +185 -25
- package/src/sdk/host/sdk-run-capability.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +581 -43
- package/src/sdk/host/session-runtime.ts +655 -101
- package/src/sdk/lifecycle/broker-client.ts +28 -0
- package/src/sdk/lifecycle/service.ts +279 -20
- package/src/sdk/lifecycle-session.ts +10 -2
- package/src/sdk/prompt-deadline-manager.ts +16 -1
- package/src/sdk/protocol/adapter-validation.ts +27 -0
- package/src/sdk/protocol/operation-inventory.generated.json +53 -2
- package/src/sdk/protocol/operation-registry.ts +17 -4
- package/src/sdk/router/session-router.ts +294 -154
- package/src/sdk/session.ts +440 -167
- package/src/sdk/startup-capability.ts +1 -0
- package/src/session/account-inventory.ts +27 -11
- package/src/session/agent-session.ts +1292 -180
- package/src/session/blob-store.ts +15 -5
- package/src/session/credential-store-errors.ts +2 -0
- package/src/session/fold-coordinator.ts +433 -0
- package/src/session/internal/managed-session-storage.ts +153 -78
- package/src/session/sdk-run-capability-internal.ts +15 -0
- package/src/session/sdk-run-capability.ts +2 -0
- package/src/session/session-manager.ts +342 -28
- package/src/session/yield-queue.ts +24 -10
- package/src/setup/frictionless-onboarding.ts +2 -0
- package/src/setup/provider-presets.json +1 -1
- package/src/slash-commands/helpers/credential-switch.ts +6 -1
- package/src/task/executor.ts +19 -3
- package/src/telemetry/control.ts +26 -0
- package/src/telemetry/events.ts +1268 -0
- package/src/telemetry/index.ts +3 -0
- package/src/telemetry/transport.ts +87 -0
- package/src/tools/bash-interactive.ts +182 -85
- package/src/tools/bash.ts +1028 -109
- package/src/tools/gh.ts +104 -48
- package/src/tools/github-cache.ts +2 -2
- package/src/tools/index.ts +11 -5
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/web/search/provider.ts +120 -11
- package/src/web/search/providers/anthropic.ts +89 -17
- package/src/web/search/providers/base.ts +9 -4
- package/src/web/search/providers/gemini.ts +87 -15
- package/src/web/search/providers/openai-compatible.ts +23 -8
- package/src/web/search/providers/xai.ts +98 -12
- package/src/web/search/types.ts +16 -0
|
@@ -2,24 +2,39 @@ import { randomBytes } from "node:crypto";
|
|
|
2
2
|
import * as fsSync from "node:fs";
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
+
import { replaceTabs, truncateToWidth } from "@gajae-code/tui";
|
|
5
6
|
import { getAgentDir } from "@gajae-code/utils";
|
|
6
7
|
import { repo as resolveGitRepository } from "../../utils/git";
|
|
7
8
|
import { ensureBroker } from "../broker/ensure";
|
|
9
|
+
import { resolveSessionLocator } from "../broker/session-index";
|
|
10
|
+
import {
|
|
11
|
+
resolveScopeRequest,
|
|
12
|
+
type ScopeNameV1,
|
|
13
|
+
type ScopeRequestV1,
|
|
14
|
+
type SdkSearchResultV1,
|
|
15
|
+
type SdkSearchRowV1,
|
|
16
|
+
} from "../broker/session-scope";
|
|
8
17
|
import { lifecycleRequestTimeoutMs } from "../broker/startup-budget";
|
|
9
|
-
import { SdkClientError } from "../client";
|
|
18
|
+
import { readSdkBrokerDiscovery, SdkClient, SdkClientError } from "../client";
|
|
10
19
|
import { createBrokerSessionLifecycleService } from "../lifecycle/broker-client";
|
|
11
20
|
import type {
|
|
12
21
|
SessionLifecycleMutationRequest,
|
|
13
22
|
SessionLifecycleOperation,
|
|
14
23
|
SessionLifecycleSavedSession,
|
|
15
24
|
SessionLifecycleSavedSessionIdentity,
|
|
25
|
+
SessionLifecycleService,
|
|
16
26
|
SessionReconcileUncertainTarget,
|
|
17
27
|
} from "../lifecycle/service";
|
|
18
28
|
import { PROMPT_CLIENT_REF_MAX_LENGTH } from "../prompt-status";
|
|
19
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
validateAdapterControl,
|
|
31
|
+
validateAdapterSecretFields,
|
|
32
|
+
validateRequiredPromptText,
|
|
33
|
+
} from "../protocol/adapter-validation";
|
|
20
34
|
import { adapterDispositionError, findOperation, type OperationKind } from "../protocol/operation-registry";
|
|
21
35
|
import { type SessionAttachment, SessionRouter, SessionRouterError, type SessionRouterFrame } from "../router";
|
|
22
36
|
import { SessionListTraversalError, sessionListPageFromResponse, traverseSessionList } from "../session-list";
|
|
37
|
+
import { SESSION_REQUEST_TIMEOUT_MS } from "../session-reconnect";
|
|
23
38
|
import {
|
|
24
39
|
type SdkCheckpointRecordV1,
|
|
25
40
|
type SdkRetentionGapV1,
|
|
@@ -35,6 +50,7 @@ import {
|
|
|
35
50
|
|
|
36
51
|
export type SdkSessionCliAction =
|
|
37
52
|
| "list"
|
|
53
|
+
| "search"
|
|
38
54
|
| "inspect"
|
|
39
55
|
| "send"
|
|
40
56
|
| "status"
|
|
@@ -60,7 +76,6 @@ export interface SdkSessionCliArgs {
|
|
|
60
76
|
jsonInputFile?: string;
|
|
61
77
|
jsonInputStdin?: boolean;
|
|
62
78
|
idempotencyKey?: string;
|
|
63
|
-
scope?: string;
|
|
64
79
|
confirm?: boolean;
|
|
65
80
|
cursor?: string;
|
|
66
81
|
wait?: boolean;
|
|
@@ -69,6 +84,10 @@ export interface SdkSessionCliArgs {
|
|
|
69
84
|
untilIdle?: boolean;
|
|
70
85
|
allEvents?: boolean;
|
|
71
86
|
repo?: string;
|
|
87
|
+
scope?: string;
|
|
88
|
+
limit?: number;
|
|
89
|
+
json?: boolean;
|
|
90
|
+
|
|
72
91
|
agentDir?: string;
|
|
73
92
|
}
|
|
74
93
|
|
|
@@ -91,6 +110,9 @@ const TAIL_OFFLINE_MAX_SCAN_BYTES = 4 * 1024 * 1024;
|
|
|
91
110
|
const TAIL_OFFLINE_MAX_SCANNED_LINES = 4_096;
|
|
92
111
|
const TAIL_OFFLINE_MAX_LINE_BYTES = 256 * 1024;
|
|
93
112
|
const transcriptDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
113
|
+
const SEARCH_PROBE_TIMEOUT_MS = 2_000;
|
|
114
|
+
const SEARCH_PROBE_MAX_ROWS = 100;
|
|
115
|
+
const SEARCH_TEXT_WIDTH = 80;
|
|
94
116
|
|
|
95
117
|
const TERMINAL_TURN_KINDS = new Set(["turn_end", "agent_end"]);
|
|
96
118
|
const START_TURN_KINDS = new Set(["turn_start", "agent_start"]);
|
|
@@ -210,6 +232,10 @@ function isEndpointOperation(operation: string): boolean {
|
|
|
210
232
|
return operation === "session.get_endpoint";
|
|
211
233
|
}
|
|
212
234
|
|
|
235
|
+
function isRawSpawnOperation(kind: OperationKind, operation: string): boolean {
|
|
236
|
+
return kind === "global" && operation === "session.spawn";
|
|
237
|
+
}
|
|
238
|
+
|
|
213
239
|
function isLifecycleOperation(operation: string): operation is LifecycleMutationOperation {
|
|
214
240
|
return (
|
|
215
241
|
operation === "session.create" ||
|
|
@@ -253,9 +279,11 @@ async function withRouter<T>(
|
|
|
253
279
|
agentDir: string,
|
|
254
280
|
action: (router: SessionRouter) => Promise<T>,
|
|
255
281
|
onFrame?: (attachment: SessionAttachment, frame: SessionRouterFrame) => void,
|
|
282
|
+
sessionIds?: readonly string[],
|
|
256
283
|
): Promise<T> {
|
|
257
284
|
const router = new SessionRouter({
|
|
258
285
|
agentDir,
|
|
286
|
+
...(sessionIds === undefined ? {} : { sessionIds }),
|
|
259
287
|
...(onFrame === undefined ? {} : { deps: { onFrame } }),
|
|
260
288
|
});
|
|
261
289
|
let result!: T;
|
|
@@ -363,6 +391,145 @@ async function sessionRows(agentDir: string): Promise<SessionRows> {
|
|
|
363
391
|
});
|
|
364
392
|
}
|
|
365
393
|
|
|
394
|
+
function searchScopeRequest(scope: ScopeNameV1, locator: { cwd: string; worktreeRoot: string | null }): ScopeRequestV1 {
|
|
395
|
+
return { version: 1, requested: scope, requestAnchor: { cwd: locator.cwd, worktreeRoot: locator.worktreeRoot } };
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function searchProbe(row: SdkSearchRowV1, router: SessionRouter): Promise<SdkSearchRowV1> {
|
|
399
|
+
if (!row.live) return Promise.resolve({ ...row, probe: "stale" });
|
|
400
|
+
return (async () => {
|
|
401
|
+
try {
|
|
402
|
+
const attachment = router.attachment(row.id);
|
|
403
|
+
if (!attachment) return { ...row, probe: "unreachable" };
|
|
404
|
+
const response = await router.request(
|
|
405
|
+
row.id,
|
|
406
|
+
{ type: "query_request", query: "session.checkpoint", input: {} },
|
|
407
|
+
attachment.generation,
|
|
408
|
+
attachment,
|
|
409
|
+
{ timeoutMs: SEARCH_PROBE_TIMEOUT_MS },
|
|
410
|
+
);
|
|
411
|
+
return { ...row, probe: response.ok === true ? "reachable" : "unreachable" };
|
|
412
|
+
} catch {
|
|
413
|
+
return { ...row, probe: "unreachable" };
|
|
414
|
+
}
|
|
415
|
+
})();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function mergeProbedSearchRows(
|
|
419
|
+
rows: readonly SdkSearchRowV1[],
|
|
420
|
+
probedRows: readonly SdkSearchRowV1[],
|
|
421
|
+
): readonly SdkSearchRowV1[] {
|
|
422
|
+
return [...probedRows, ...rows.slice(probedRows.length)];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async function probeSearchRows(agentDir: string, result: SdkSearchResultV1): Promise<SdkSearchResultV1> {
|
|
426
|
+
if (result.status === "unavailable" || result.status === "not-in-git-worktree" || result.rows.length === 0)
|
|
427
|
+
return result;
|
|
428
|
+
const rows = result.rows.slice(0, SEARCH_PROBE_MAX_ROWS);
|
|
429
|
+
try {
|
|
430
|
+
const probes = await withRouter(
|
|
431
|
+
agentDir,
|
|
432
|
+
async router => await Promise.all(rows.map(row => searchProbe(row, router))),
|
|
433
|
+
undefined,
|
|
434
|
+
rows.map(row => row.id),
|
|
435
|
+
);
|
|
436
|
+
return { ...result, rows: mergeProbedSearchRows(result.rows, probes) };
|
|
437
|
+
} catch {
|
|
438
|
+
return {
|
|
439
|
+
...result,
|
|
440
|
+
rows: mergeProbedSearchRows(
|
|
441
|
+
result.rows,
|
|
442
|
+
rows.map(row => ({ ...row, probe: row.live ? "unreachable" : "stale" })),
|
|
443
|
+
),
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/** Resolves, lists, and probes only the exact rows selected by the Broker-scoped search. */
|
|
449
|
+
export async function runSdkSearch(
|
|
450
|
+
args: Pick<SdkSessionCliArgs, "agentDir" | "repo" | "scope" | "limit" | "cursor">,
|
|
451
|
+
createService: (agentDir: string) => SessionLifecycleService = createBrokerSessionLifecycleService,
|
|
452
|
+
probe: (agentDir: string, result: SdkSearchResultV1) => Promise<SdkSearchResultV1> = probeSearchRows,
|
|
453
|
+
): Promise<{ result: SdkSearchResultV1; exitCode: 0 | 1 }> {
|
|
454
|
+
const agentDir = args.agentDir ?? getAgentDir();
|
|
455
|
+
const locator = await resolveSessionLocator(args.repo ?? process.cwd(), agentDir);
|
|
456
|
+
const scope: ScopeNameV1 =
|
|
457
|
+
args.scope === undefined || args.scope === "repo" || args.scope === "pwd" || args.scope === "global"
|
|
458
|
+
? ((args.scope ?? "repo") as ScopeNameV1)
|
|
459
|
+
: (() => {
|
|
460
|
+
throw new SdkSessionCliError(
|
|
461
|
+
"usage",
|
|
462
|
+
`Invalid search scope "${args.scope}". Expected repo, pwd, or global.`,
|
|
463
|
+
2,
|
|
464
|
+
);
|
|
465
|
+
})();
|
|
466
|
+
const request = searchScopeRequest(scope, locator);
|
|
467
|
+
const resolved = await resolveScopeRequest(request);
|
|
468
|
+
const outcome = await createService(agentDir).scopedList(request, args.limit, args.cursor);
|
|
469
|
+
if (outcome.ok) {
|
|
470
|
+
if ("rows" in outcome.result) {
|
|
471
|
+
if (outcome.result.status === "not-in-git-worktree") return { result: outcome.result, exitCode: 0 };
|
|
472
|
+
if (outcome.result.status === "unavailable") return { result: outcome.result, exitCode: 1 };
|
|
473
|
+
return { result: await probe(agentDir, outcome.result), exitCode: 0 };
|
|
474
|
+
}
|
|
475
|
+
return {
|
|
476
|
+
result: {
|
|
477
|
+
version: 1,
|
|
478
|
+
scope: resolved,
|
|
479
|
+
status: "unavailable",
|
|
480
|
+
observedAt: new Date().toISOString(),
|
|
481
|
+
rows: [],
|
|
482
|
+
warnings: [],
|
|
483
|
+
error: { code: "malformed_response", message: "broker search returned an unscoped result" },
|
|
484
|
+
},
|
|
485
|
+
exitCode: 1,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
if (!outcome.ok && outcome.result?.status === "unavailable") return { result: outcome.result, exitCode: 1 };
|
|
489
|
+
return {
|
|
490
|
+
result: {
|
|
491
|
+
version: 1,
|
|
492
|
+
scope: resolved,
|
|
493
|
+
status: "unavailable",
|
|
494
|
+
observedAt: new Date().toISOString(),
|
|
495
|
+
rows: [],
|
|
496
|
+
warnings: [],
|
|
497
|
+
error: { code: outcome.error.code, message: outcome.error.message },
|
|
498
|
+
},
|
|
499
|
+
exitCode: 1,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function searchScopeLabel(result: SdkSearchResultV1): string {
|
|
504
|
+
const resolved = result.scope.resolved;
|
|
505
|
+
if (resolved === null) return "not-in-git-worktree";
|
|
506
|
+
if (resolved.kind === "repo") return resolved.worktreeRoot;
|
|
507
|
+
if (resolved.kind === "pwd") return resolved.cwd;
|
|
508
|
+
return resolved.visibility;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function safeSearchText(value: string): string {
|
|
512
|
+
return truncateToWidth(replaceTabs(value).replaceAll(/[\r\n]/g, " "), SEARCH_TEXT_WIDTH);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/** Renders a credential-free scope/status preamble and bounded search table. */
|
|
516
|
+
export function renderSdkSearchTable(result: SdkSearchResultV1): string {
|
|
517
|
+
const lines = [
|
|
518
|
+
`Scope requested: ${result.scope.requested}`,
|
|
519
|
+
`Scope resolved: ${safeSearchText(searchScopeLabel(result))}`,
|
|
520
|
+
`Status: ${result.status}`,
|
|
521
|
+
`Observed at: ${result.observedAt}`,
|
|
522
|
+
...(result.cursor === undefined ? [] : [`Continuation cursor: ${safeSearchText(result.cursor)}`]),
|
|
523
|
+
];
|
|
524
|
+
if (result.rows.length === 0) return lines.join("\n");
|
|
525
|
+
lines.push("ID PROBE LIVE CWD");
|
|
526
|
+
for (const row of result.rows)
|
|
527
|
+
lines.push(
|
|
528
|
+
`${safeSearchText(row.id).padEnd(20)} ${(row.probe ?? "-").padEnd(11)} ${String(row.live).padEnd(4)} ${safeSearchText(row.locator.cwd)}`,
|
|
529
|
+
);
|
|
530
|
+
return lines.join("\n");
|
|
531
|
+
}
|
|
532
|
+
|
|
366
533
|
const SESSION_LIST_SCOPES: readonly SdkSessionListScope[] = ["repo", "cwd", "worktree", "all"];
|
|
367
534
|
/** Row workspaces whose locator cannot be canonicalized still get one deterministic identity. */
|
|
368
535
|
const WORKSPACE_IDENTITY_CACHE_LIMIT = 4096;
|
|
@@ -483,7 +650,7 @@ export async function filterSessionRowsByScope(
|
|
|
483
650
|
const warnings: string[] = [];
|
|
484
651
|
const sessions: SdkSessionRowV1[] = [];
|
|
485
652
|
for (const row of rows) {
|
|
486
|
-
const identity = await workspaceIdentity(row.locator.
|
|
653
|
+
const identity = await workspaceIdentity(row.locator.cwd);
|
|
487
654
|
let keep: boolean;
|
|
488
655
|
if (scope === "cwd") keep = identity.canonicalPath === selection.selection.canonicalPath;
|
|
489
656
|
else if (scope === "worktree")
|
|
@@ -492,7 +659,7 @@ export async function filterSessionRowsByScope(
|
|
|
492
659
|
if (keep) sessions.push(row);
|
|
493
660
|
else if (identity.repoRoot === null && identity.commonDir === null)
|
|
494
661
|
warnings.push(
|
|
495
|
-
`Session ${row.sessionId} workspace "${row.locator.
|
|
662
|
+
`Session ${row.sessionId} workspace "${row.locator.cwd}" is outside Git; excluded by scope ${scope}.`,
|
|
496
663
|
);
|
|
497
664
|
}
|
|
498
665
|
return { sessions, warnings };
|
|
@@ -559,7 +726,7 @@ async function requestControl(
|
|
|
559
726
|
const attachment = attachmentFor(router, sessionId);
|
|
560
727
|
const response = await router.request(
|
|
561
728
|
sessionId,
|
|
562
|
-
|
|
729
|
+
controlRequestFrame(operation, input, args),
|
|
563
730
|
attachment.generation,
|
|
564
731
|
attachment,
|
|
565
732
|
args.timeoutMs === undefined ? undefined : { timeoutMs: args.timeoutMs },
|
|
@@ -568,6 +735,81 @@ async function requestControl(
|
|
|
568
735
|
return response;
|
|
569
736
|
}
|
|
570
737
|
|
|
738
|
+
/** Builds the public CLI control envelope without leaking envelope fields into operation input. */
|
|
739
|
+
export function controlRequestFrame(
|
|
740
|
+
operation: string,
|
|
741
|
+
input: JsonRecord,
|
|
742
|
+
args: Pick<SdkSessionCliArgs, "confirm" | "idempotencyKey">,
|
|
743
|
+
): JsonRecord {
|
|
744
|
+
return {
|
|
745
|
+
type: "control_request",
|
|
746
|
+
operation,
|
|
747
|
+
input,
|
|
748
|
+
confirm: args.confirm === true,
|
|
749
|
+
...(args.idempotencyKey ? { idempotencyKey: args.idempotencyKey } : {}),
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const BROKER_OPERATOR_ABORT_FIELDS = new Set(["mode", "scope", "operator"]);
|
|
754
|
+
|
|
755
|
+
export function operatorAbortBrokerRequest(
|
|
756
|
+
sessionId: string,
|
|
757
|
+
operation: string,
|
|
758
|
+
input: JsonRecord,
|
|
759
|
+
args: Pick<SdkSessionCliArgs, "confirm" | "idempotencyKey">,
|
|
760
|
+
): JsonRecord | undefined {
|
|
761
|
+
if (!Object.hasOwn(input, "operator")) return undefined;
|
|
762
|
+
if (operation !== "turn.abort")
|
|
763
|
+
throw new SdkSessionCliError("invalid_input", "operator is only available for terminal turn.abort.", 2);
|
|
764
|
+
if (input.operator !== true)
|
|
765
|
+
throw new SdkSessionCliError("invalid_input", "turn.abort operator must be true when provided.", 2);
|
|
766
|
+
if (input.mode !== "terminal")
|
|
767
|
+
throw new SdkSessionCliError("invalid_input", 'operator turn.abort requires mode:"terminal".', 2);
|
|
768
|
+
for (const key of Object.keys(input))
|
|
769
|
+
if (!BROKER_OPERATOR_ABORT_FIELDS.has(key))
|
|
770
|
+
throw new SdkSessionCliError(`invalid_input`, `Unknown turn.abort terminal field: ${key}`, 2);
|
|
771
|
+
if (input.scope !== undefined && input.scope !== "turn" && input.scope !== "owned")
|
|
772
|
+
throw new SdkSessionCliError("invalid_input", 'operator turn.abort scope must be "turn" or "owned".', 2);
|
|
773
|
+
if (args.confirm !== true)
|
|
774
|
+
throw new SdkSessionCliError("invalid_input", "operator terminal abort requires --confirm.", 2);
|
|
775
|
+
if (!args.idempotencyKey)
|
|
776
|
+
throw new SdkSessionCliError("invalid_input", "operator terminal abort requires --idempotency-key.", 2);
|
|
777
|
+
return { sessionId, operation: "turn.abort", input, confirm: true };
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
async function requestBrokerOperatorAbort(
|
|
781
|
+
agentDir: string,
|
|
782
|
+
request: JsonRecord,
|
|
783
|
+
args: SdkSessionCliArgs,
|
|
784
|
+
): Promise<JsonRecord> {
|
|
785
|
+
const idempotencyKey = args.idempotencyKey;
|
|
786
|
+
if (!idempotencyKey)
|
|
787
|
+
throw new SdkSessionCliError("invalid_input", "operator terminal abort requires --idempotency-key.", 2);
|
|
788
|
+
const discovery = await readSdkBrokerDiscovery(agentDir);
|
|
789
|
+
if (!discovery) throw new SdkSessionCliError("session_unavailable", "SDK broker discovery is unavailable.", 1);
|
|
790
|
+
const timeoutMs = args.timeoutMs ?? SESSION_REQUEST_TIMEOUT_MS;
|
|
791
|
+
const client = await SdkClient.connect(discovery.url, discovery.token, {
|
|
792
|
+
timeoutMs,
|
|
793
|
+
reconnectAttempts: 0,
|
|
794
|
+
});
|
|
795
|
+
try {
|
|
796
|
+
const response = await client.global("session.control", request, {
|
|
797
|
+
idempotencyKey,
|
|
798
|
+
timeoutMs,
|
|
799
|
+
});
|
|
800
|
+
const result = object(response);
|
|
801
|
+
if (!result) throw new SdkClientError("protocol_error", "SDK broker returned a malformed control response.");
|
|
802
|
+
throwResponseFailure(result);
|
|
803
|
+
return result;
|
|
804
|
+
} finally {
|
|
805
|
+
await client.close().catch(error => {
|
|
806
|
+
process.stderr.write(
|
|
807
|
+
`SDK broker control cleanup failed: ${error instanceof Error ? error.message : String(error)}\n`,
|
|
808
|
+
);
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
571
813
|
async function requestQuery(
|
|
572
814
|
router: SessionRouter,
|
|
573
815
|
sessionId: string,
|
|
@@ -654,6 +896,8 @@ async function runSend(agentDir: string, sessionId: string, args: SdkSessionCliA
|
|
|
654
896
|
if (args.text !== undefined && Object.keys(input).length > 0)
|
|
655
897
|
throw new SdkSessionCliError("usage", "Use either --text or one JSON input source for the prompt, not both.", 2);
|
|
656
898
|
const promptInput: JsonRecord = args.text === undefined ? { ...input } : { text: args.text };
|
|
899
|
+
const promptError = validateRequiredPromptText("turn.prompt", promptInput);
|
|
900
|
+
if (promptError) throw new SdkSessionCliError(promptError.code, promptError.message, 2);
|
|
657
901
|
const inputRef = clientRefFromInput(promptInput);
|
|
658
902
|
const clientRef = inputRef ?? args.opRef?.trim() ?? createOperationRef();
|
|
659
903
|
if (args.opRef !== undefined && inputRef !== undefined && inputRef !== args.opRef.trim())
|
|
@@ -914,6 +1158,11 @@ function publicationSequence(publicationId: string): number | undefined {
|
|
|
914
1158
|
|
|
915
1159
|
function tailItemFromRouterFrame(frame: SessionRouterFrame): SdkTailItemV1 | undefined {
|
|
916
1160
|
if (frame.name === undefined) return undefined;
|
|
1161
|
+
// The replay response is an out-of-band control frame, not an event-ring
|
|
1162
|
+
// item. Its generation/lastSeq metadata is a response position, and it
|
|
1163
|
+
// intentionally has no event `seq`; projecting it as a tail item would make
|
|
1164
|
+
// the strict event-position validator reject every otherwise valid tail.
|
|
1165
|
+
if (frame.name === "event_replay_result") return undefined;
|
|
917
1166
|
const seq = frame.publicationId === undefined ? undefined : publicationSequence(frame.publicationId);
|
|
918
1167
|
return toTailItemV1(
|
|
919
1168
|
{
|
|
@@ -1110,6 +1359,12 @@ async function offlineTailReplay(
|
|
|
1110
1359
|
});
|
|
1111
1360
|
if (!outcome.ok)
|
|
1112
1361
|
throw new SdkSessionCliError(outcome.error.code, outcome.error.message, 1, { certainty: outcome.certainty });
|
|
1362
|
+
if ("rows" in outcome.result)
|
|
1363
|
+
throw new SdkSessionCliError(
|
|
1364
|
+
"malformed_response",
|
|
1365
|
+
"broker returned a scoped result for retained transcript replay",
|
|
1366
|
+
1,
|
|
1367
|
+
);
|
|
1113
1368
|
const savedSession = outcome.result.savedSession;
|
|
1114
1369
|
if (!savedSession)
|
|
1115
1370
|
throw new SdkSessionCliError(
|
|
@@ -1402,6 +1657,8 @@ async function runRawControl(
|
|
|
1402
1657
|
const invalid = validateAdapterControl(operation, input);
|
|
1403
1658
|
if (invalid) throw new SdkSessionCliError(invalid.code, invalid.message, 2);
|
|
1404
1659
|
await ensureBroker({ agentDir });
|
|
1660
|
+
const operatorRequest = operatorAbortBrokerRequest(sessionId, operation, input, args);
|
|
1661
|
+
if (operatorRequest) return await requestBrokerOperatorAbort(agentDir, operatorRequest, args);
|
|
1405
1662
|
return await withRouter(agentDir, async router => await requestControl(router, sessionId, operation, input, args));
|
|
1406
1663
|
}
|
|
1407
1664
|
|
|
@@ -1495,6 +1752,7 @@ export async function runSdkSessionCli(
|
|
|
1495
1752
|
const action = args.action;
|
|
1496
1753
|
if (
|
|
1497
1754
|
action !== "list" &&
|
|
1755
|
+
action !== "search" &&
|
|
1498
1756
|
action !== "inspect" &&
|
|
1499
1757
|
action !== "send" &&
|
|
1500
1758
|
action !== "status" &&
|
|
@@ -1515,6 +1773,12 @@ export async function runSdkSessionCli(
|
|
|
1515
1773
|
writeOutput(stripSecretFields(await runList(agentDir, args)));
|
|
1516
1774
|
return;
|
|
1517
1775
|
}
|
|
1776
|
+
if (action === "search") {
|
|
1777
|
+
const search = await runSdkSearch(args);
|
|
1778
|
+
writeOutput(args.json === true ? search.result : renderSdkSearchTable(search.result));
|
|
1779
|
+
if (search.exitCode !== 0) setExitCode(search.exitCode);
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1518
1782
|
if (action === "inspect") {
|
|
1519
1783
|
writeOutput(stripSecretFields(await runInspect(agentDir, requireValue(args.sessionId, "<sessionId>"))));
|
|
1520
1784
|
return;
|
|
@@ -1566,6 +1830,12 @@ export async function runSdkSessionCli(
|
|
|
1566
1830
|
const kind = rawKind(requireValue(action, "<verb>"), args);
|
|
1567
1831
|
if (!kind) throw new SdkSessionCliError("usage", "raw requires one of: control, query, global.", 2);
|
|
1568
1832
|
const operation = kind === "query" ? requireValue(args.query, "--query") : requireValue(args.operation, "--op");
|
|
1833
|
+
if (isRawSpawnOperation(kind, operation))
|
|
1834
|
+
throw new SdkSessionCliError(
|
|
1835
|
+
"adapter_operation_prohibited",
|
|
1836
|
+
"session.spawn is unavailable through the SDK session CLI.",
|
|
1837
|
+
1,
|
|
1838
|
+
);
|
|
1569
1839
|
const dispositionError = cliOperationError(kind, operation);
|
|
1570
1840
|
if (dispositionError) throw new SdkSessionCliError(dispositionError.code, dispositionError.message, 1);
|
|
1571
1841
|
if (isEndpointOperation(operation))
|
package/src/sdk/client/client.ts
CHANGED
|
@@ -116,13 +116,13 @@ export interface SdkDispatchContext {
|
|
|
116
116
|
readonly generation: number;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
/** Request identity retained after an uncertain send
|
|
120
|
-
|
|
119
|
+
/** Request identity retained after an uncertain send. */
|
|
121
120
|
export interface SdkSentRecord {
|
|
122
121
|
readonly id: string;
|
|
123
122
|
readonly operation?: string;
|
|
124
123
|
readonly idempotencyKey?: string;
|
|
125
|
-
|
|
124
|
+
/** Present for lifecycle lookups; `session.spawn` replays through its idempotency key. */
|
|
125
|
+
readonly fingerprint?: string;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
export type SdkFrameHandler = (frame: SdkFrame) => void;
|
|
@@ -473,8 +473,10 @@ export class SdkClient {
|
|
|
473
473
|
const sentOperation = typeof serializedFrame.operation === "string" ? serializedFrame.operation : undefined;
|
|
474
474
|
const sentIdempotencyKey =
|
|
475
475
|
typeof serializedFrame.idempotencyKey === "string" ? serializedFrame.idempotencyKey : undefined;
|
|
476
|
-
const
|
|
477
|
-
|
|
476
|
+
const isSpawn = sentOperation === "session.spawn";
|
|
477
|
+
const sentFingerprint = isSpawn
|
|
478
|
+
? undefined
|
|
479
|
+
: typeof serializedFrame.operation === "string"
|
|
478
480
|
? lifecycleFingerprint(serializedFrame.operation, serializedFrame.input ?? {})
|
|
479
481
|
: inputFingerprint(serializedFrame.input ?? {});
|
|
480
482
|
const deferred = Promise.withResolvers<unknown>();
|
|
@@ -571,11 +573,15 @@ export class SdkClient {
|
|
|
571
573
|
// nothing reached the transport, so the request stays retryable and
|
|
572
574
|
// non-uncertain with no record retained.
|
|
573
575
|
pending.sent = true;
|
|
576
|
+
// A spawn claim replays only through its caller-provided idempotency key;
|
|
577
|
+
// it deliberately has no lifecycle fingerprint. Retain the common sent
|
|
578
|
+
// record anyway so an uncertain transport result tells the caller which
|
|
579
|
+
// key can join that durable claim.
|
|
574
580
|
this.#rememberSentRecord({
|
|
575
581
|
id,
|
|
576
|
-
operation: sentOperation,
|
|
577
|
-
idempotencyKey: sentIdempotencyKey,
|
|
578
|
-
fingerprint: sentFingerprint,
|
|
582
|
+
...(sentOperation === undefined ? {} : { operation: sentOperation }),
|
|
583
|
+
...(sentIdempotencyKey === undefined ? {} : { idempotencyKey: sentIdempotencyKey }),
|
|
584
|
+
...(sentFingerprint === undefined ? {} : { fingerprint: sentFingerprint }),
|
|
579
585
|
});
|
|
580
586
|
try {
|
|
581
587
|
incarnation.socket.send(serializedRequest);
|
|
@@ -35,7 +35,7 @@ export class SdkDiscoveryError extends Error {
|
|
|
35
35
|
|
|
36
36
|
export type SdkSessionEndpointScope = "default" | "chat";
|
|
37
37
|
|
|
38
|
-
function endpointDirectory(repo: string, scope: SdkSessionEndpointScope = "default"): string {
|
|
38
|
+
export function endpointDirectory(repo: string, scope: SdkSessionEndpointScope = "default"): string {
|
|
39
39
|
return scope === "chat" ? path.join(repo, ".gjc", "state", "chat", "sdk") : path.join(repo, ".gjc", "state", "sdk");
|
|
40
40
|
}
|
|
41
41
|
function isUsableSessionId(sessionId: string): boolean {
|
|
@@ -132,6 +132,8 @@ export async function readSdkSessionEndpoint(
|
|
|
132
132
|
if (!isUsableSessionId(sessionId)) return null;
|
|
133
133
|
const file = path.join(endpointDirectory(repo, scope), `${sessionId}.json`);
|
|
134
134
|
try {
|
|
135
|
+
const stat = await fs.lstat(file);
|
|
136
|
+
if (!stat.isFile()) return null;
|
|
135
137
|
return parseEndpoint(sessionId, file, JSON.parse(await fs.readFile(file, "utf8")));
|
|
136
138
|
} catch (error) {
|
|
137
139
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|