@gajae-code/coding-agent 0.18.8 → 0.18.10
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 +100 -0
- package/dist/types/async/endpoint-id.d.ts +16 -0
- package/dist/types/async/support.d.ts +0 -14
- package/dist/types/commands/mcp-serve.d.ts +4 -0
- package/dist/types/discovery/mcp-json.d.ts +1 -0
- package/dist/types/eval/py/kernel.d.ts +18 -0
- package/dist/types/export/html/html-export-destination.d.ts +2 -0
- package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
- package/dist/types/extensibility/skill-management.d.ts +1 -0
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -3
- package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +3 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
- package/dist/types/lsp/config.d.ts +8 -5
- package/dist/types/runtime-mcp/manager.d.ts +2 -1
- package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -1
- package/dist/types/runtime-mcp/project-host-values.d.ts +27 -0
- package/dist/types/sdk/broker/broker.d.ts +6 -1
- package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
- package/dist/types/sdk/broker/ensure.d.ts +14 -1
- package/dist/types/session/session-storage.d.ts +7 -0
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +3 -0
- package/dist/types/task/isolation-dir.d.ts +26 -0
- package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
- package/dist/types/tools/browser/cdp-url.d.ts +2 -0
- package/dist/types/tools/github-side-effect.d.ts +22 -0
- package/dist/types/tools/monitor-mutation.d.ts +9 -0
- package/package.json +7 -7
- package/src/async/endpoint-id.ts +26 -0
- package/src/async/job-manager.ts +19 -10
- package/src/async/support.ts +0 -25
- package/src/capability/index.ts +14 -16
- package/src/cli/doctor/repairs.ts +4 -1
- package/src/cli/doctor-supervisor.ts +3 -0
- package/src/cli/public-command-registry.ts +28 -6
- package/src/commands/mcp-serve.ts +7 -0
- package/src/commands/sdk.ts +453 -415
- package/src/config/file-lock.ts +9 -11
- package/src/config/model-profile-activation.ts +5 -5
- package/src/coordinator-mcp/server.ts +2 -0
- package/src/customization/inventory.ts +1 -13
- package/src/dap/config.ts +2 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +10 -6
- package/src/discovery/builtin.ts +3 -1
- package/src/discovery/helpers.ts +12 -9
- package/src/discovery/mcp-json.ts +20 -12
- package/src/eval/py/executor.ts +459 -77
- package/src/eval/py/kernel.ts +79 -13
- package/src/eval/py/tool-bridge.ts +4 -0
- package/src/export/html/html-export-destination.ts +16 -0
- package/src/export/html/index.ts +2 -6
- package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
- package/src/extensibility/extensions/runner.ts +11 -1
- package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +27 -15
- package/src/extensibility/skill-management.ts +3 -0
- package/src/gjc-runtime/launch-worktree.ts +56 -5
- package/src/gjc-runtime/psmux-detect.ts +28 -4
- package/src/gjc-runtime/session-state-sidecar.ts +3 -2
- package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
- package/src/gjc-runtime/state-writer.ts +22 -8
- package/src/gjc-runtime/tmux-common.ts +8 -5
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/lsp/config.ts +12 -63
- package/src/memories/index.ts +88 -7
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/hook-selector.ts +1 -1
- package/src/modes/controllers/input-controller.ts +2 -2
- package/src/modes/controllers/runtime-mcp-command-controller.ts +21 -13
- package/src/modes/controllers/selector-controller.ts +3 -7
- package/src/modes/controllers/todo-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +1 -1
- package/src/runtime-mcp/client.ts +4 -2
- package/src/runtime-mcp/json-rpc.ts +2 -1
- package/src/runtime-mcp/manager.ts +29 -11
- package/src/runtime-mcp/mcp-redirect.ts +73 -0
- package/src/runtime-mcp/oauth-flow.ts +11 -4
- package/src/runtime-mcp/oauth-public-url.ts +8 -2
- package/src/runtime-mcp/pool.ts +6 -0
- package/src/runtime-mcp/project-host-values.ts +46 -0
- package/src/runtime-mcp/transports/http.ts +2 -1
- package/src/sdk/acp/adapter.ts +2 -0
- package/src/sdk/broker/broker.ts +6 -0
- package/src/sdk/broker/doctor-restart.ts +4 -0
- package/src/sdk/broker/ensure.ts +29 -2
- package/src/sdk/host/session-runtime.test.ts +58 -9
- package/src/sdk/host/session-runtime.ts +65 -35
- package/src/sdk/router/session-router.ts +7 -6
- package/src/sdk/session.ts +2 -7
- package/src/session/agent-session.ts +59 -17
- package/src/session/artifacts.ts +3 -2
- package/src/session/internal/managed-session-scope.ts +403 -81
- package/src/session/internal/managed-session-storage.ts +125 -17
- package/src/session/session-manager.ts +12 -0
- package/src/session/session-storage.ts +30 -1
- package/src/skill-state/active-state.ts +13 -0
- package/src/skill-state/workflow-mutation-guard.ts +102 -10
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/slash-commands/helpers/mcp.ts +8 -4
- package/src/task/isolation-dir.ts +143 -0
- package/src/task/worktree.ts +16 -7
- package/src/tools/ast-edit-write-path.ts +68 -0
- package/src/tools/ast-edit.ts +7 -2
- package/src/tools/browser/cdp-url.ts +18 -0
- package/src/tools/browser.ts +8 -1
- package/src/tools/gh.ts +10 -0
- package/src/tools/github-side-effect.ts +112 -0
- package/src/tools/monitor-mutation.ts +27 -0
- package/src/tools/monitor.ts +7 -0
- package/src/tools/python.ts +7 -6
- package/src/utils/external-editor.ts +3 -2
- package/src/web/insane/url-guard.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,106 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.18.10] - 2026-10-12
|
|
6
|
+
|
|
7
|
+
## [0.18.9] - 2026-10-11
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added attach-only SDK client mode: with `GJC_SDK_BROKER_AUTOSTART=0` or `--attach-only` (on `gjc sdk session`, `gjc sdk search`, `gjc sdk spawn` and `gjc mcp-serve`), every SDK client entry point (session CLI, search, spawn, SDK and coordinator MCP servers, ACP, session hosts) attaches only to a live, reusable broker and fails with `broker_unavailable` instead of spawning, retiring, or restarting one, so a supervisor-owned broker has a single owner. `gjc doctor` broker restart refuses with `attach_only_refused`, and the SDK broker restart script refuses before closing hosts or stopping the broker.
|
|
12
|
+
|
|
13
|
+
- Added `gjc sdk broker run [--agent-dir <dir>]`, a stable foreground broker command for supervisors (systemd, launchd). It never detaches, stops gracefully on SIGTERM or SIGINT and exits with the signal status, exits 1 instead of serving when another live broker already owns the agent directory (never retiring it), and exits 1 if it loses its discovery root while serving.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Prevented deferred startup profile recovery from replacing newer model or role selections during credential refresh or settings flush; rejected model selections no longer cancel recovery, and canceled recovery restores session-owned alias and fallback state (#6380).
|
|
18
|
+
|
|
19
|
+
- Match Windows session paths by filesystem identity without rewriting canonical paths or conflating distinct case-sensitive files. Managed-root containment canonicalizes aliases above the root and rejects symlink components below it.
|
|
20
|
+
- Reject managed descendant symlinks before canonicalizing paths, preventing a replaced scope directory from redirecting access to a sibling session.
|
|
21
|
+
- Keep native-backed endpoint key derivation out of the broadly imported async barrel so idle startup does not load path identity bindings.
|
|
22
|
+
|
|
23
|
+
- The Bash tool now counts characters instead of UTF-8 bytes for `${#var}`, so negative substring offsets such as `${var: -2}` select the right characters in non-ASCII values (#6544).
|
|
24
|
+
- A negative substring length now marks where the slice ends, as in bash (`${v:2:-2}` on `abcdefgh` is `cdef`), and an end before the offset or a negative length on an array fails with `substring expression < 0` instead of slicing the wrong range.
|
|
25
|
+
|
|
26
|
+
- `ast_edit` apply refuses auto-generated files when that edit guard is on, the same as write and vim.
|
|
27
|
+
|
|
28
|
+
- `ast_edit` apply checks the real write path and the original preview path, so a directory symlink such as `src` → `.gjc` cannot bypass the `.gjc/**` block, and a `.gjc` directory that points at another in-workspace directory stays blocked.
|
|
29
|
+
|
|
30
|
+
- Enroll async-job delivery and loop completion futures before callbacks and change notifications so drains and bounded disposal cannot mistake in-flight work for settled delivery.
|
|
31
|
+
|
|
32
|
+
- Contain rejections from `async` agent-session event subscribers, so a failed subscriber write (for example the `managed_append_identity_mismatch` fence after another process resumed the same session) is logged instead of terminating the interactive process with an unhandled rejection.
|
|
33
|
+
|
|
34
|
+
- `app.cdp_url` is refused unless its host is loopback.
|
|
35
|
+
|
|
36
|
+
- Debug adapter lookup uses the same trusted `$PATH` rule as LSP auto-detection, so a repository `bin/`, `node_modules/.bin`, or virtualenv binary is no longer spawned as a debugger.
|
|
37
|
+
|
|
38
|
+
- Managed-session descriptor preflight and bounded range reads now preserve their original metadata, pathname, or range error when descriptor close also fails; close-only errors after a successful read still propagate.
|
|
39
|
+
|
|
40
|
+
- Reusing a detached launch worktree now refuses to check out a new source HEAD when the worktree commit is not an ancestor of that HEAD, so committed work is not silently dropped.
|
|
41
|
+
|
|
42
|
+
- Explicit session directories create fork and branch transcripts and unsent drafts with the same native owner-only security as the primary transcript.
|
|
43
|
+
|
|
44
|
+
- A project `.env` declaration of `VISUAL` or `EDITOR` no longer selects the external editor that GJC executes on your draft; values inherited from the launching shell or GJC/user-owned env files are still honored.
|
|
45
|
+
|
|
46
|
+
- Temp directory cleanup on Windows: sessions now properly release managed sidecar cache file handles during `closeStrict()`, preventing "directory in use" errors when deleting temporary session directories in tests. The root cause was unconditional resource retention when close succeeded; the fix ensures file handles are released even when resident blob cleanup is skipped (#6566).
|
|
47
|
+
|
|
48
|
+
- Side-effect `github` operations (`pr_create`, `pr_checkout`, `pr_push`) now go through the session approval gate and the planning-phase mutation guard, while read-only operations stay available.
|
|
49
|
+
|
|
50
|
+
- HTML session export creates and replaces its output file as owner-only (`0600`).
|
|
51
|
+
|
|
52
|
+
- Keep bundled Grok Build request hooks scoped to their provider so unrelated SDK sessions retain replay-safe empty-response fallback. Cover the production SDK observer, accepted-only lifecycle, managed transcript persistence, and billed empty stops without changing deadline reconciliation or admitting rejected attempts.
|
|
53
|
+
|
|
54
|
+
- Preserve clean managed-session fallback admission when the SDK observes the run start, while keeping user extension execution replay-unsafe. Successful empty stops with nonzero usage no longer trigger legacy proxy-overflow retries in session-owned turns.
|
|
55
|
+
|
|
56
|
+
- Plugin MCP policy denies the shared CGNAT range 100.64.0.0/10, matching the public URL address check.
|
|
57
|
+
|
|
58
|
+
- Remote MCP HTTP no longer follows a redirect onto another origin, so custom credential headers, the MCP session id, and the JSON-RPC body stay on the configured server.
|
|
59
|
+
|
|
60
|
+
- A modern MCP `roots/list` input request uses the connection's request handler, so a tools-only session does not answer with the project URI.
|
|
61
|
+
|
|
62
|
+
- Project MCP config no longer runs shell substitutions or copies secret-named environment variables into server environment and headers.
|
|
63
|
+
|
|
64
|
+
- Local memory consolidation (phase 2) no longer runs out of room under a fixed 8192-token response cap that, on adaptive-thinking and reasoning-effort models, also had to hold the reasoning; on large memories the rewritten `MEMORY.md`, summary and skills were cut off, failed as `phase2 JSON parse failure`, and `MEMORY.md` / `memory_summary.md` silently stopped updating. The response budget is now the model's output limit capped at 64k, clamped so input, response and any provider-added thinking budget fit the context window (inputs are shrunk on small-context models rather than dropping below the old 8192 budget), and phase 2 requests low reasoning effort like phase 1. A length-truncated response is recorded as `phase2 output truncated …` and a response with no text as `phase2 returned no text (stopReason=…, … output tokens, … thinking blocks)` instead of a JSON parse failure. A job that already exhausted its retries runs again once new sessions advance its input.
|
|
65
|
+
|
|
66
|
+
- The workflow mutation guard no longer treats an out-of-band mode-state phase as permission to edit product code while the active planning skill is still sealed.
|
|
67
|
+
|
|
68
|
+
- `monitor` commands are checked by the same planning-phase mutation guard as `bash` before a background job starts.
|
|
69
|
+
|
|
70
|
+
- The MCP OAuth client_id probe does not GET a non-public authorization URL: the request is pinned to the validated address and the flow abort signal reaches that DNS lookup. The shared address check rejects 192.88.99.0/24, including the non-global 6a44 relay 192.88.99.2.
|
|
71
|
+
|
|
72
|
+
- OAuth dynamic registration metadata is fetched only from a public URL, and that fetch does not follow redirects.
|
|
73
|
+
|
|
74
|
+
- Plugin MCP launchers no longer trust startup `node` executables located under temporary roots, even when the GJC checkout is also under `/tmp`; transient first-use digest failures can be retried against the original startup identity.
|
|
75
|
+
|
|
76
|
+
- The Python tool bridge checks that its registration is still current and not aborted after the request body arrives.
|
|
77
|
+
|
|
78
|
+
- Register spawned Python kernels before startup callbacks and retain failed or unconfirmed cleanup resources for an explicit retry instead of losing or replacing them.
|
|
79
|
+
- Separate cancelled requests from shared kernel initialization while preserving captured cleanup joins and removing failed provisional acquisitions.
|
|
80
|
+
- Preserve direct-start recovery handles and execution/cleanup error precedence, revalidate cancelled retry acquisitions, and keep active fallback owners through failed explicit preflight.
|
|
81
|
+
|
|
82
|
+
- Enroll the original Python tool invocation before external execution tracking, so reentrant clear and registered cleanup join its transcript and result finalization without clearing an independent generation.
|
|
83
|
+
|
|
84
|
+
- Remove temporary deadline-recovery stderr diagnostics so SDK prompt bookkeeping stays within centralized logging and does not expose correlation identifiers through debug prints.
|
|
85
|
+
|
|
86
|
+
- A retained managed session directory replaced by a symlink is now refused as a changed root binding, and the newly retained child store is closed.
|
|
87
|
+
|
|
88
|
+
- Bound shared GC count/collect inventories across each read-only receipt read, discovery call and protocol inspection, including unrelated entries and repeated passes. Preserve genuine leased publication quotas, authority fences and descriptor admission; report capacity refusal rather than omitting targets. This does not establish full GC traversal/read/hash admission or writer quiescence.
|
|
89
|
+
|
|
90
|
+
- The coordinator sidecar ignores a signing-key URL or key that the project dotenv declares, so a repository `.env` cannot supply the signing key.
|
|
91
|
+
|
|
92
|
+
- Task isolation directories are unique per attempt, so one session can no longer delete another session's live worktree when both reuse the same task id.
|
|
93
|
+
|
|
94
|
+
- A project `.env` declaration of `GJC_TMUX_COMMAND`, including a dynamic one, no longer selects the tmux binary; the default `tmux` path is used instead, while an operator value the project does not declare is kept.
|
|
95
|
+
|
|
96
|
+
- Preserve trusted user-skill symlink provenance in isolated-home discovery, honor the effective scope trust policy, and reuse scanner-validated import metadata in customization inventory.
|
|
97
|
+
|
|
98
|
+
- Fixed Windows CI task cache key test cleanup failures (EBUSY/EPERM) by adding exponential backoff retry logic to temporary directory removal. File handles on Windows may remain open during deletion, causing removal to fail; retrying with increasing delays resolves the locking issue.
|
|
99
|
+
|
|
100
|
+
- A project `.env` declaration of `GJC_WORKTREE_DIR`, including a dynamic one, no longer selects the launch worktree bucket; the default `{repo}/.worktrees` path is used instead, while an operator value the project does not declare is kept.
|
|
101
|
+
|
|
102
|
+
- Zero-usage empty stops from context-overflow proxies trigger context promotion and overflow recovery again; only successful empty stops with nonzero usage stay terminal in session-owned turns.
|
|
103
|
+
- Total-token-only empty stops (from OpenAI-compatible endpoints returning only `usage.total_tokens`) are now correctly classified as nonzero usage and remain terminal, preventing unnecessary promotion or retry.
|
|
104
|
+
|
|
5
105
|
## [0.18.8] - 2026-10-08
|
|
6
106
|
|
|
7
107
|
### Added
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the AsyncJobManager endpoint key.
|
|
3
|
+
*
|
|
4
|
+
* A session with an explicit provider session id is keyed by (provider id,
|
|
5
|
+
* transcript path); everything else is keyed by its logical session id. The
|
|
6
|
+
* transcript component uses a stable path key so a path alias — symlink,
|
|
7
|
+
* casing difference on a case-insensitive Windows directory, or `..` segment —
|
|
8
|
+
* cannot register the manager under one key at construction and look it up
|
|
9
|
+
* under another across a session-identity transition, stranding ownership.
|
|
10
|
+
* The key remains stable when a transcript is created or atomically replaced,
|
|
11
|
+
* and case-sensitive Windows paths remain distinct.
|
|
12
|
+
*
|
|
13
|
+
* This module is deliberately not re-exported from the broadly imported async
|
|
14
|
+
* barrel: importing the path identity helper loads native bindings.
|
|
15
|
+
*/
|
|
16
|
+
export declare function asyncJobEndpointId(providerSessionId: string | undefined, sessionId: string, sessionFile: string | undefined): string;
|
|
@@ -1,16 +1,2 @@
|
|
|
1
1
|
import type { Settings } from "../config/settings";
|
|
2
2
|
export declare function isBackgroundJobSupportEnabled(settings: Pick<Settings, "get">): boolean;
|
|
3
|
-
/**
|
|
4
|
-
* Single source of truth for the AsyncJobManager endpoint key.
|
|
5
|
-
*
|
|
6
|
-
* A session with an explicit provider session id is keyed by (provider id,
|
|
7
|
-
* transcript path); everything else is keyed by its logical session id. The
|
|
8
|
-
* transcript component is canonicalized so a path alias — symlink, `..`
|
|
9
|
-
* segment, or unresolved relative path — cannot register the manager under one
|
|
10
|
-
* key at construction and look it up under another across a session-identity
|
|
11
|
-
* transition, stranding ownership.
|
|
12
|
-
*
|
|
13
|
-
* Both the SDK constructor and AgentSession's transition rekeying must derive
|
|
14
|
-
* the key here; duplicating the shape lets the two drift apart silently.
|
|
15
|
-
*/
|
|
16
|
-
export declare function asyncJobEndpointId(providerSessionId: string | undefined, sessionId: string, sessionFile: string | undefined): string;
|
|
@@ -52,6 +52,10 @@ export default class McpServe extends Command {
|
|
|
52
52
|
description: string;
|
|
53
53
|
default: boolean;
|
|
54
54
|
};
|
|
55
|
+
"attach-only": import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
56
|
+
description: string;
|
|
57
|
+
default: boolean;
|
|
58
|
+
};
|
|
55
59
|
};
|
|
56
60
|
run(): Promise<void>;
|
|
57
61
|
}
|
|
@@ -42,6 +42,24 @@ interface KernelStartOptions extends KernelLifecycleOptions {
|
|
|
42
42
|
env?: Record<string, string | undefined>;
|
|
43
43
|
runtimeOptions?: PythonRuntimeOptions;
|
|
44
44
|
settings?: Settings;
|
|
45
|
+
/** @internal Synchronously registers the actual spawned kernel before startup callbacks run. */
|
|
46
|
+
onKernelCreated?: (kernel: PythonKernel) => void;
|
|
47
|
+
/** @internal Routes partial-start cleanup through the executor's retained ownership. */
|
|
48
|
+
onStartupFailure?: (kernel: PythonKernel) => Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
export type PythonKernelStartupCleanupFailure = {
|
|
51
|
+
kind: "unconfirmed";
|
|
52
|
+
result: KernelShutdownResult;
|
|
53
|
+
} | {
|
|
54
|
+
kind: "rejected";
|
|
55
|
+
error: unknown;
|
|
56
|
+
};
|
|
57
|
+
/** Startup failed after a real runner was created and physical cleanup failed. */
|
|
58
|
+
export declare class PythonKernelStartError extends Error {
|
|
59
|
+
readonly kernel: PythonKernel;
|
|
60
|
+
readonly startupError: unknown;
|
|
61
|
+
readonly cleanupFailure: PythonKernelStartupCleanupFailure;
|
|
62
|
+
constructor(kernel: PythonKernel, startupError: unknown, cleanupFailure: PythonKernelStartupCleanupFailure);
|
|
45
63
|
}
|
|
46
64
|
interface KernelShutdownOptions {
|
|
47
65
|
signal?: AbortSignal;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { type FunctionHookRegistration } from "./function-hooks";
|
|
2
2
|
export type TaggedFunctionHookHandler = (...args: unknown[]) => Promise<unknown>;
|
|
3
|
+
/** Host-owned provider adapters must not consume replay authority for other providers. */
|
|
4
|
+
export declare function tagProviderRequestHandler<T extends (...args: never[]) => unknown>(provider: string, handler: T): T;
|
|
5
|
+
export declare function getProviderRequestHandlerProvider(handler: (...args: never[]) => unknown): string | undefined;
|
|
6
|
+
export declare function tagSdkLifecycleObserver<T extends (...args: never[]) => unknown>(handler: T): T;
|
|
7
|
+
export declare function isSdkLifecycleObserver(handler: (...args: never[]) => unknown): boolean;
|
|
3
8
|
export declare function tagExtensionHandlerRegistrationOrder<T extends (...args: never[]) => unknown>(handler: T, registrationOrder: number): T;
|
|
4
9
|
export declare function wrapExtensionHandlerRegistration<T extends (...args: never[]) => unknown>(handler: T, registrationOrder: number): T;
|
|
5
10
|
export declare function getExtensionHandlerRegistrationOrder(value: unknown): number | undefined;
|
|
@@ -13,6 +13,7 @@ export type SkillDisabledReason = "protected" | "scope-trust" | "ignored" | "inc
|
|
|
13
13
|
export interface ManagedSkillRecord {
|
|
14
14
|
name: string;
|
|
15
15
|
description: string;
|
|
16
|
+
importedFrom?: string;
|
|
16
17
|
path: string;
|
|
17
18
|
scope: SkillScope;
|
|
18
19
|
/** Canonical source label, e.g. "project .gjc/skills" or "user ~/.gjc/agent/skills". */
|
|
@@ -64,9 +64,9 @@ export interface ResolvedTmuxBinary {
|
|
|
64
64
|
viaExplicitOverride: boolean;
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
|
-
* Resolve the tmux command GJC should invoke. Honors
|
|
68
|
-
* GJC_TMUX_COMMAND override;
|
|
69
|
-
* override is set, psmux (installed as psmux, pmux, or tmux) is picked
|
|
67
|
+
* Resolve the tmux command GJC should invoke. Honors an operator
|
|
68
|
+
* GJC_TMUX_COMMAND override; a project dotenv value is ignored. On Windows
|
|
69
|
+
* when no override is set, psmux (installed as psmux, pmux, or tmux) is picked
|
|
70
70
|
* automatically so the default gjc --tmux flow lands on a real multiplexer.
|
|
71
71
|
*/
|
|
72
72
|
export declare function resolveGjcTmuxBinary(options?: ResolveGjcTmuxBinaryOptions): ResolvedTmuxBinary;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ProjectEnvSnapshot } from "@gajae-code/utils/env-file";
|
|
2
|
+
/**
|
|
3
|
+
* Operator environment only. A value that matches the project dotenv snapshot
|
|
4
|
+
* was loaded by Bun from the repository and must not become signing material.
|
|
5
|
+
*/
|
|
6
|
+
export declare function trustedCoordinatorEnv(name: string, env?: NodeJS.ProcessEnv, snapshot?: ProjectEnvSnapshot): string | undefined;
|
|
@@ -168,6 +168,9 @@ export type StrictMutationReadResult = {
|
|
|
168
168
|
export declare function readExistingStateForMutation(filePath: string): Promise<StrictMutationReadResult>;
|
|
169
169
|
export declare function workflowEnvelopeContentSha256(value: unknown): string;
|
|
170
170
|
export declare function stampWorkflowEnvelopeChecksum<T>(value: T, filePath: string, computedAt?: string): T;
|
|
171
|
+
export type WorkflowEnvelopeChecksumStatus = "absent" | "match" | "mismatch";
|
|
172
|
+
/** Compare a parsed envelope to its writer-stamped `content_sha256`, when one is present. */
|
|
173
|
+
export declare function workflowEnvelopeChecksumStatus(value: unknown): WorkflowEnvelopeChecksumStatus;
|
|
171
174
|
export declare function detectWorkflowEnvelopeIntegrityMismatch(filePath: string): Promise<WorkflowEnvelopeIntegrityMismatch | undefined>;
|
|
172
175
|
type ActiveStateCacheInvalidator = (cwd?: string, sessionId?: string) => void;
|
|
173
176
|
export declare function setActiveStateCacheInvalidator(invalidator: ActiveStateCacheInvalidator): void;
|
|
@@ -40,11 +40,14 @@ export declare function envDisabled(value: string | undefined): boolean;
|
|
|
40
40
|
* failure mode where `gjc --tmux` creates a psmux-backed session and then
|
|
41
41
|
* `gjc session status` fails because it queries literal `tmux` is closed off.
|
|
42
42
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
43
|
+
* An operator `GJC_TMUX_COMMAND` is honored on every platform. A value that
|
|
44
|
+
* matches the project dotenv snapshot, including a dynamic declaration, is
|
|
45
|
+
* ignored so a repository cannot choose the binary, and the resolver uses the
|
|
46
|
+
* same default as when the variable is unset. On native Windows without an
|
|
47
|
+
* override the resolver walks `psmux`, then `pmux`, then `tmux` and uses the
|
|
48
|
+
* first binary present on PATH. On POSIX the resolver returns `tmux` (the
|
|
49
|
+
* historical default) and only falls through to the platform-aware walker if
|
|
50
|
+
* the caller opts in.
|
|
48
51
|
*/
|
|
49
52
|
export declare function resolveGjcTmuxCommand(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string;
|
|
50
53
|
export type { PsmuxProbe, ResolvedTmuxBinary, ResolveGjcTmuxBinaryOptions } from "./psmux-detect";
|
|
@@ -9,13 +9,16 @@ export interface LspConfig {
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function hasRootMarkers(cwd: string, markers: string[]): boolean;
|
|
11
11
|
/**
|
|
12
|
-
* Resolve a
|
|
13
|
-
*
|
|
12
|
+
* Resolve a host-spawn executable from $PATH without consulting project-controlled bin directories.
|
|
13
|
+
* LSP auto-detection and DAP adapter launch both use this rule.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Trust is evaluated on both the discovered and canonical paths, but the returned
|
|
16
|
+
* path is the discovered one: multiplexed launchers such as rustup pick the proxied
|
|
17
|
+
* tool from argv[0], so `~/.cargo/bin/rust-analyzer -> rustup` must be spawned as
|
|
18
|
+
* `rust-analyzer`, not as the resolved `rustup` binary.
|
|
18
19
|
*/
|
|
20
|
+
export declare function resolveTrustedCommand(command: string, cwd: string): string | null;
|
|
21
|
+
/** Same rule as `resolveTrustedCommand`: project-local bin directories are never searched. */
|
|
19
22
|
export declare function resolveCommand(command: string, cwd: string): string | null;
|
|
20
23
|
/**
|
|
21
24
|
* Load LSP configuration.
|
|
@@ -257,10 +257,11 @@ export declare class MCPManager {
|
|
|
257
257
|
/**
|
|
258
258
|
* Resolve auth and shell-command substitutions in config before connecting.
|
|
259
259
|
*/
|
|
260
|
-
prepareConfig(config: MCPServerConfig): Promise<MCPServerConfig>;
|
|
260
|
+
prepareConfig(config: MCPServerConfig, source?: SourceMeta): Promise<MCPServerConfig>;
|
|
261
261
|
/** Acquire a prepared, pool-owned lease for a scoped transient operation. */
|
|
262
262
|
withPreparedLease<T>(name: string, config: MCPServerConfig, fn: (lease: MCPPoolLease) => Promise<T> | T, options?: {
|
|
263
263
|
signal?: AbortSignal;
|
|
264
|
+
source?: SourceMeta;
|
|
264
265
|
}): Promise<T>;
|
|
265
266
|
/** Read-only test seam for pending retired lease-release records. */
|
|
266
267
|
get retiredLeaseReleaseCountForTests(): number;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Follow same-origin MCP redirects only.
|
|
3
|
+
*
|
|
4
|
+
* A cross-origin 307/308 would carry custom credential headers, `Mcp-Session-Id`,
|
|
5
|
+
* and the JSON-RPC body. The first request uses `rawUrl` unchanged so a caller
|
|
6
|
+
* that cannot be parsed (diagnostic redaction) still reaches fetch, but with
|
|
7
|
+
* `redirect: "manual"` so that hop is not followed either.
|
|
8
|
+
*/
|
|
9
|
+
export declare function fetchMcpRespectingOrigin(rawUrl: string, init: BunFetchRequestInit): Promise<Response>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-value policy for MCP config that comes from an untrusted project file.
|
|
3
|
+
*
|
|
4
|
+
* User mcp.json and an explicit `--mcp-config` file may still substitute
|
|
5
|
+
* secret-named environment variables and run `!` shell commands. A project
|
|
6
|
+
* file (native `.gjc/mcp.json` or a repo-root `mcp.json`) uses the same
|
|
7
|
+
* sensitive-name rule as project `ssh.json`: do not copy those host values
|
|
8
|
+
* into server env or headers, and do not execute `!`.
|
|
9
|
+
*/
|
|
10
|
+
import type { SourceMeta } from "../capability/types";
|
|
11
|
+
export declare function nativeMcpSource(level: "user" | "project", filePath: string): SourceMeta;
|
|
12
|
+
/** True when this server was loaded from untrusted project MCP config. */
|
|
13
|
+
export declare function isUntrustedProjectMcpSource(source: SourceMeta | undefined, toolsOnly: boolean): boolean;
|
|
14
|
+
export type ProjectHostValueDecision = {
|
|
15
|
+
action: "resolve";
|
|
16
|
+
} | {
|
|
17
|
+
action: "drop";
|
|
18
|
+
} | {
|
|
19
|
+
action: "literal";
|
|
20
|
+
value: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Decide how to interpret one env or header value.
|
|
24
|
+
* `drop` means "do not run the shell, and omit the value".
|
|
25
|
+
* `literal` means "do not read process.env".
|
|
26
|
+
*/
|
|
27
|
+
export declare function decideUntrustedProjectConfigValue(value: string, untrusted: boolean): ProjectHostValueDecision;
|
|
@@ -3,7 +3,7 @@ import type { ModelProfileErrorDetails } from "../../config/model-profile-contra
|
|
|
3
3
|
import type { TaskArtifactOwnerDeletionEvidence, TaskArtifactOwnerRetirementContinuation } from "../../session/task-artifact-owner-codec";
|
|
4
4
|
import { type SdkHostModelResolver } from "../host/model-pin";
|
|
5
5
|
import { type DirectoryMigrationPolicy } from "../session-directory";
|
|
6
|
-
import { type BrokerStopRequest } from "./broker-exit";
|
|
6
|
+
import { type BrokerExitMode, type BrokerExitReason, type BrokerStopRequest } from "./broker-exit";
|
|
7
7
|
import { type BrokerDiscovery, type BrokerPublicationObservation, type RedactedBrokerDiscovery } from "./discovery";
|
|
8
8
|
import { type LifecycleDurableEffectsReceipt, LifecycleLedger, type LifecycleStartupFailureReceipt } from "./lifecycle-ledger";
|
|
9
9
|
import { type IndexedSession, SessionIndex } from "./session-index";
|
|
@@ -373,6 +373,11 @@ export declare class Broker {
|
|
|
373
373
|
start(): Promise<BrokerDiscovery>;
|
|
374
374
|
get ownsDiscovery(): boolean;
|
|
375
375
|
get completion(): Promise<void>;
|
|
376
|
+
/** Why this broker completed, or null while it is still running. */
|
|
377
|
+
get terminalExit(): {
|
|
378
|
+
readonly mode: BrokerExitMode;
|
|
379
|
+
readonly reason: BrokerExitReason;
|
|
380
|
+
} | null;
|
|
376
381
|
prepareRestart(options: BrokerRestartPrepareOptions): Promise<BrokerRestartResult>;
|
|
377
382
|
commitRestart(options: BrokerRestartCommitOptions): Promise<BrokerRestartResult>;
|
|
378
383
|
cancelRestart(requestId: string): Promise<BrokerRestartResult>;
|
|
@@ -23,6 +23,11 @@ export type DoctorBrokerRestartOutcome = {
|
|
|
23
23
|
} | {
|
|
24
24
|
kind: "owner_unavailable";
|
|
25
25
|
reason: "no_discovery" | "owner_not_confirmed_live";
|
|
26
|
+
}
|
|
27
|
+
/** Attach-only mode: the broker belongs to an external supervisor, so restart through it. */
|
|
28
|
+
| {
|
|
29
|
+
kind: "attach_only_refused";
|
|
30
|
+
reason: "supervisor_owned";
|
|
26
31
|
} | {
|
|
27
32
|
kind: "prepare_refused";
|
|
28
33
|
code: string;
|
|
@@ -152,7 +152,20 @@ export declare function closeBrokerClientBeforeDeadline(client: {
|
|
|
152
152
|
}, deadline: number): Promise<void>;
|
|
153
153
|
/** Reconcile an observed broker generation before entering a startup critical section. */
|
|
154
154
|
export declare function reconcileBrokerGenerationForStartup(settings: EnsureBrokerSettings, deadline?: number): Promise<BrokerDiscovery | undefined>;
|
|
155
|
-
/**
|
|
155
|
+
/**
|
|
156
|
+
* Attach-only client mode. When `GJC_SDK_BROKER_AUTOSTART=0`, `ensureBroker`
|
|
157
|
+
* never spawns, retires or restarts a broker: it returns a live, reusable
|
|
158
|
+
* discovery or fails with `broker_unavailable`. Hosts that supervise their own
|
|
159
|
+
* broker (for example a system service) set it for every client they run. The
|
|
160
|
+
* broker entrypoints never call `ensureBroker`, so they ignore the variable and
|
|
161
|
+
* their descendants may inherit it.
|
|
162
|
+
*/
|
|
163
|
+
export declare const SDK_BROKER_AUTOSTART_ENV = "GJC_SDK_BROKER_AUTOSTART";
|
|
164
|
+
export declare function isBrokerAttachOnly(env?: NodeJS.ProcessEnv): boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Starts the detached broker entrypoint when discovery has no live owner. In
|
|
167
|
+
* attach-only mode (`SDK_BROKER_AUTOSTART_ENV`) it only attaches.
|
|
168
|
+
*/
|
|
156
169
|
export declare function ensureBroker(settings: EnsureBrokerSettings): Promise<BrokerDiscovery>;
|
|
157
170
|
/** Starts one fresh fixture broker and returns its sole exact-child close lease. */
|
|
158
171
|
export declare function startFixtureBrokerWithLeaseForTest(settings: EnsureBrokerSettings): Promise<StartedFixtureBroker>;
|
|
@@ -444,6 +444,13 @@ export type NativeDirectoryTreeSnapshot = {
|
|
|
444
444
|
rootIno: string;
|
|
445
445
|
entries: NativeDirectoryTreeEntry[];
|
|
446
446
|
};
|
|
447
|
+
/**
|
|
448
|
+
* Replace an explicit session file. Creation mode `0600` does not install a
|
|
449
|
+
* Windows owner-only DACL, so this applies and verifies the same native path a
|
|
450
|
+
* primary transcript uses when it has no managed security context. POSIX mode
|
|
451
|
+
* stays `0600`.
|
|
452
|
+
*/
|
|
453
|
+
export declare function writeExplicitOwnerOnlyTextSync(pathname: string, content: string): void;
|
|
447
454
|
/** Physically observed commit-marker state; corrupt JSON is still `present`. */
|
|
448
455
|
export type SessionCommitMarkerState = {
|
|
449
456
|
kind: "missing";
|
|
@@ -5,6 +5,9 @@ export declare const WORKFLOW_STATE_MUTATION_BLOCK_MESSAGE = ".gjc workflow stat
|
|
|
5
5
|
export declare const RALPLAN_MUTATION_BLOCK_MESSAGE = "Ralplan planning phase boundary: keep refining the consensus plan and persist plan artifacts through `gjc ralplan --write` (stage scratch files under a temp dir if needed). Product-code mutation tools and patch execution are blocked while ralplan is active; mutate only after the plan is approved and execution begins.";
|
|
6
6
|
export declare const ULTRAGOAL_GOAL_PLANNING_MUTATION_BLOCK_MESSAGE = "Ultragoal goal-planning phase boundary: finish goal planning and record goals through `gjc ultragoal` before editing code. Product-code mutation tools and patch execution are blocked until goal planning completes and execution begins.";
|
|
7
7
|
export declare const AUTORESEARCH_MUTATION_BLOCK_MESSAGE = "Autoresearch research-only boundary: this workflow produces findings, evidence, and a verdict \u2014 never product code. Product-code mutation tools and patch execution are blocked everywhere, on every branch. Mission research artifacts (`autoresearch.sh` at the workdir root and mission state through the sanctioned `gjc autoresearch` CLI) stay writable; run experiments through the `python` tool, or finish the mission and hand off to an approved implementation workflow.";
|
|
8
|
+
/** Tools whose execute path can mutate product state. `github` is included so side-effect ops share this guard. */
|
|
9
|
+
export declare const WORKFLOW_MUTATION_TOOL_NAMES: readonly ["edit", "write", "ast_edit", "bash", "github"];
|
|
10
|
+
export declare function isWorkflowMutationTool(name: string): boolean;
|
|
8
11
|
type ToolWithEditMode = AgentTool & {
|
|
9
12
|
mode?: unknown;
|
|
10
13
|
customWireName?: unknown;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
type PathApi = Pick<typeof path, "dirname" | "isAbsolute" | "relative" | "resolve" | "sep">;
|
|
3
|
+
/**
|
|
4
|
+
* Parent directory of `mergedDir`, but only when it is a single entry inside
|
|
5
|
+
* `worktreeRoot`. Root, empty, nested, and escaped paths are refused so
|
|
6
|
+
* cleanup cannot delete the worktree root or another session's tree by
|
|
7
|
+
* walking up.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveIsolationRemovalTarget(mergedDir: string, worktreeRoot: string, pathApi?: PathApi): string;
|
|
10
|
+
/**
|
|
11
|
+
* Create an exclusive directory next to `canonicalBaseDir`. Never removes
|
|
12
|
+
* `canonicalBaseDir` or any path that already exists.
|
|
13
|
+
*/
|
|
14
|
+
export declare function allocateDisjointIsolationDir(canonicalBaseDir: string, tokenFactory?: () => string): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Confirm native teardown may see `mergedDir`.
|
|
17
|
+
*
|
|
18
|
+
* `isoStop` deletes that path. The parent entry and `merged` itself must
|
|
19
|
+
* already be real directories, because native removal follows symlinks.
|
|
20
|
+
* A missing parent means there is nothing to stop. This does not close a
|
|
21
|
+
* replacement that happens after the lstat returns.
|
|
22
|
+
*/
|
|
23
|
+
export declare function assertIsolationTeardownTarget(mergedDir: string, worktreeRoot: string): Promise<"stop" | "skip">;
|
|
24
|
+
/** Remove the isolation directory that owns `mergedDir`. Missing directories are ignored. */
|
|
25
|
+
export declare function removeIsolationDirectory(mergedDir: string, worktreeRoot: string): Promise<void>;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path the native writer will mutate: realpath of an existing file, or realpath
|
|
3
|
+
* of the nearest existing ancestor plus the missing tail.
|
|
4
|
+
*/
|
|
5
|
+
export declare function canonicalizeAstEditWritePath(absolutePath: string): Promise<string>;
|
|
6
|
+
/**
|
|
7
|
+
* Preview paths as the workflow guard must see them.
|
|
8
|
+
*
|
|
9
|
+
* The guard compares lexically against the session cwd. A raw realpath misses
|
|
10
|
+
* `.gjc/**` when that cwd is not already canonical (`/var` vs `/private/var`,
|
|
11
|
+
* or a symlinked workspace), so an in-workspace real path is reattached to `cwd`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveAstEditPreviewWritePaths(cwd: string, previewedPaths: readonly string[]): Promise<string[]>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classify `github` tool operations for the approval gate and the planning
|
|
3
|
+
* mutation guard. Read-only ops stay available. Every other op, including a
|
|
4
|
+
* missing or unrecognized one, is a side effect and fails closed.
|
|
5
|
+
*/
|
|
6
|
+
import type { AgentTool } from "@gajae-code/agent-core";
|
|
7
|
+
/** Name-only tool the planning guard uses when `GithubTool.execute` checks itself. */
|
|
8
|
+
export declare const githubMutationTool: AgentTool;
|
|
9
|
+
export declare function githubOp(args: unknown): string | undefined;
|
|
10
|
+
/** True only for a known read-only github op. Missing and unknown ops are not read-only. */
|
|
11
|
+
export declare function isGithubReadOnlyArgs(args: unknown): boolean;
|
|
12
|
+
export declare function isGithubReadOnlyOp(op: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Approval summary for a side-effect github call. The PR body is omitted so a
|
|
15
|
+
* token pasted into `body` is not copied into the permission title.
|
|
16
|
+
*/
|
|
17
|
+
export declare function describeGithubSideEffect(args: unknown): string;
|
|
18
|
+
export declare function githubPermissionIntent(args: unknown): {
|
|
19
|
+
toolName: string;
|
|
20
|
+
title: string;
|
|
21
|
+
cacheKey: string;
|
|
22
|
+
} | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Judge a monitor command with the same mutation rules as bash. Monitor is a
|
|
3
|
+
* background shell, so a planning-phase mutation must fail before the job starts.
|
|
4
|
+
*/
|
|
5
|
+
export declare function assertMonitorMutationAllowed(input: {
|
|
6
|
+
cwd: string;
|
|
7
|
+
sessionId?: string;
|
|
8
|
+
command: string;
|
|
9
|
+
}): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.18.
|
|
4
|
+
"version": "0.18.10",
|
|
5
5
|
"description": "Gajae Code CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo and Gajae Code Contributors",
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
58
58
|
"@babel/parser": "^7.29.3",
|
|
59
59
|
"@mozilla/readability": "^0.6.0",
|
|
60
|
-
"@gajae-code/stats": "0.18.
|
|
61
|
-
"@gajae-code/agent-core": "0.18.
|
|
62
|
-
"@gajae-code/ai": "0.18.
|
|
63
|
-
"@gajae-code/natives": "0.18.
|
|
64
|
-
"@gajae-code/tui": "0.18.
|
|
65
|
-
"@gajae-code/utils": "0.18.
|
|
60
|
+
"@gajae-code/stats": "0.18.10",
|
|
61
|
+
"@gajae-code/agent-core": "0.18.10",
|
|
62
|
+
"@gajae-code/ai": "0.18.10",
|
|
63
|
+
"@gajae-code/natives": "0.18.10",
|
|
64
|
+
"@gajae-code/tui": "0.18.10",
|
|
65
|
+
"@gajae-code/utils": "0.18.10",
|
|
66
66
|
"@puppeteer/browsers": "^2.13.0",
|
|
67
67
|
"@types/turndown": "5.0.6",
|
|
68
68
|
"@xterm/headless": "^6.0.0",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { stablePathKey } from "@gajae-code/utils/path-identity";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Single source of truth for the AsyncJobManager endpoint key.
|
|
5
|
+
*
|
|
6
|
+
* A session with an explicit provider session id is keyed by (provider id,
|
|
7
|
+
* transcript path); everything else is keyed by its logical session id. The
|
|
8
|
+
* transcript component uses a stable path key so a path alias — symlink,
|
|
9
|
+
* casing difference on a case-insensitive Windows directory, or `..` segment —
|
|
10
|
+
* cannot register the manager under one key at construction and look it up
|
|
11
|
+
* under another across a session-identity transition, stranding ownership.
|
|
12
|
+
* The key remains stable when a transcript is created or atomically replaced,
|
|
13
|
+
* and case-sensitive Windows paths remain distinct.
|
|
14
|
+
*
|
|
15
|
+
* This module is deliberately not re-exported from the broadly imported async
|
|
16
|
+
* barrel: importing the path identity helper loads native bindings.
|
|
17
|
+
*/
|
|
18
|
+
export function asyncJobEndpointId(
|
|
19
|
+
providerSessionId: string | undefined,
|
|
20
|
+
sessionId: string,
|
|
21
|
+
sessionFile: string | undefined,
|
|
22
|
+
): string {
|
|
23
|
+
return providerSessionId !== undefined && sessionFile !== undefined
|
|
24
|
+
? JSON.stringify(["async-job-endpoint", providerSessionId, stablePathKey(sessionFile)])
|
|
25
|
+
: sessionId;
|
|
26
|
+
}
|