@bastani/atomic 0.9.40 → 0.9.41-alpha.2
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 +75 -0
- package/README.md +1 -1
- package/dist/builtin/intercom/CHANGELOG.md +12 -0
- package/dist/builtin/intercom/README.md +1 -1
- package/dist/builtin/intercom/broker/broker.ts +24 -21
- package/dist/builtin/intercom/broker/client.ts +97 -6
- package/dist/builtin/intercom/broker/list-reply.ts +53 -0
- package/dist/builtin/intercom/index.bundle.mjs +278 -46
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/intercom/recoverable-disconnect.ts +27 -1
- package/dist/builtin/intercom/types.ts +8 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +14 -0
- package/dist/builtin/subagents/README.md +14 -0
- package/dist/builtin/subagents/package.json +3 -3
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +99 -19
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +33 -0
- package/dist/builtin/workflows/README.md +2 -0
- package/dist/builtin/workflows/builtin/adversarial-verification.js +2 -2
- package/dist/builtin/workflows/builtin/{chunk-qjqn389g.js → chunk-2jfst7e4.js} +72 -10
- package/dist/builtin/workflows/builtin/{chunk-4bwtzyjq.js → chunk-49kkbdpr.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-qdd897wm.js → chunk-apagcs4r.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-454hz1qt.js → chunk-dcxscfnb.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-bjva9j5k.js → chunk-hx8v4p97.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-85mx6wcf.js → chunk-k5q3t7je.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-8kkv04hz.js → chunk-n2gkzkty.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-y45tkftg.js → chunk-txbc93t6.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-x0vvjyzg.js → chunk-w4xp1w1f.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-5eh0brtd.js → chunk-z3pajrfs.js} +1 -1
- package/dist/builtin/workflows/builtin/classify-and-act.js +2 -2
- package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +2 -2
- package/dist/builtin/workflows/builtin/generate-and-filter.js +2 -2
- package/dist/builtin/workflows/builtin/goal.js +2 -2
- package/dist/builtin/workflows/builtin/index.js +10 -10
- package/dist/builtin/workflows/builtin/loop-until-done.js +2 -2
- package/dist/builtin/workflows/builtin/open-claude-design.js +2 -2
- package/dist/builtin/workflows/builtin/ralph.js +2 -2
- package/dist/builtin/workflows/builtin/tournament.js +2 -2
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/src/authoring.d.ts +1 -1
- package/dist/builtin/workflows/src/durable/journal/internal.d.ts +16 -0
- package/dist/builtin/workflows/src/durable/journal/legacy-journal-tables.d.ts +9 -0
- package/dist/builtin/workflows/src/durable/journal/sql-run-journal.d.ts +6 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +950 -116
- package/dist/builtin/workflows/src/index.js +806 -131
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +17 -1
- package/dist/cli/credential-print.d.ts.map +1 -1
- package/dist/cli/credential-print.js +1 -1
- package/dist/cli/credential-print.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts +2 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +33 -18
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +2 -1
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +114 -21
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +6 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-state.d.ts +19 -1
- package/dist/core/agent-session-state.d.ts.map +1 -1
- package/dist/core/agent-session-state.js +41 -6
- package/dist/core/agent-session-state.js.map +1 -1
- package/dist/core/auth-storage-backends.d.ts +3 -1
- package/dist/core/auth-storage-backends.d.ts.map +1 -1
- package/dist/core/auth-storage-backends.js +79 -12
- package/dist/core/auth-storage-backends.js.map +1 -1
- package/dist/core/client-identity.d.ts +8 -0
- package/dist/core/client-identity.d.ts.map +1 -0
- package/dist/core/client-identity.js +16 -0
- package/dist/core/client-identity.js.map +1 -0
- package/dist/core/compaction/planner-outcome.d.ts.map +1 -1
- package/dist/core/compaction/planner-outcome.js +2 -1
- package/dist/core/compaction/planner-outcome.js.map +1 -1
- package/dist/core/execution-model-router.d.ts +18 -0
- package/dist/core/execution-model-router.d.ts.map +1 -1
- package/dist/core/execution-model-router.js +14 -8
- package/dist/core/execution-model-router.js.map +1 -1
- package/dist/core/extension-oauth-refresh-rejection.d.ts +12 -0
- package/dist/core/extension-oauth-refresh-rejection.d.ts.map +1 -0
- package/dist/core/extension-oauth-refresh-rejection.js +70 -0
- package/dist/core/extension-oauth-refresh-rejection.js.map +1 -0
- package/dist/core/extension-startup-failure.d.ts +30 -0
- package/dist/core/extension-startup-failure.d.ts.map +1 -0
- package/dist/core/extension-startup-failure.js +62 -0
- package/dist/core/extension-startup-failure.js.map +1 -0
- package/dist/core/extensions/context-types.d.ts +2 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/provider-types.d.ts +2 -0
- package/dist/core/extensions/provider-types.d.ts.map +1 -1
- package/dist/core/extensions/provider-types.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts +1 -0
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +8 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +3 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/runtime-types.d.ts +1 -0
- package/dist/core/extensions/runtime-types.d.ts.map +1 -1
- package/dist/core/extensions/runtime-types.js.map +1 -1
- package/dist/core/extensions/workflow-run-control-hub.d.ts +1 -0
- package/dist/core/extensions/workflow-run-control-hub.d.ts.map +1 -1
- package/dist/core/extensions/workflow-run-control-hub.js +3 -0
- package/dist/core/extensions/workflow-run-control-hub.js.map +1 -1
- package/dist/core/extensions/workflow-run-control.d.ts +8 -0
- package/dist/core/extensions/workflow-run-control.d.ts.map +1 -1
- package/dist/core/extensions/workflow-run-control.js.map +1 -1
- package/dist/core/model-config.d.ts +28 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-fallback-failures.d.ts.map +1 -1
- package/dist/core/model-fallback-failures.js +12 -0
- package/dist/core/model-fallback-failures.js.map +1 -1
- package/dist/core/model-resolver-defaults.js +1 -1
- package/dist/core/model-resolver-defaults.js.map +1 -1
- package/dist/core/model-runtime-streaming.d.ts.map +1 -1
- package/dist/core/model-runtime-streaming.js +9 -3
- package/dist/core/model-runtime-streaming.js.map +1 -1
- package/dist/core/model-runtime.d.ts +5 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +6 -0
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/provider-composer-internal.d.ts +8 -6
- package/dist/core/provider-composer-internal.d.ts.map +1 -1
- package/dist/core/provider-composer-internal.js +21 -8
- package/dist/core/provider-composer-internal.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +12 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/storage/session/sql-session-store.d.ts +35 -0
- package/dist/core/storage/session/sql-session-store.d.ts.map +1 -1
- package/dist/core/storage/session/sql-session-store.js +158 -42
- package/dist/core/storage/session/sql-session-store.js.map +1 -1
- package/dist/core/structured-output/deadline.d.ts +40 -0
- package/dist/core/structured-output/deadline.d.ts.map +1 -0
- package/dist/core/structured-output/deadline.js +110 -0
- package/dist/core/structured-output/deadline.js.map +1 -0
- package/dist/core/structured-output/index.d.ts.map +1 -1
- package/dist/core/structured-output/index.js +103 -47
- package/dist/core/structured-output/index.js.map +1 -1
- package/dist/core/structured-output/rejection-detail.d.ts +9 -0
- package/dist/core/structured-output/rejection-detail.d.ts.map +1 -0
- package/dist/core/structured-output/rejection-detail.js +129 -0
- package/dist/core/structured-output/rejection-detail.js.map +1 -0
- package/dist/core/structured-output/types.d.ts +8 -1
- package/dist/core/structured-output/types.d.ts.map +1 -1
- package/dist/core/structured-output/types.js.map +1 -1
- package/dist/core/tasks/completion.d.ts +7 -1
- package/dist/core/tasks/completion.d.ts.map +1 -1
- package/dist/core/tasks/completion.js +28 -6
- package/dist/core/tasks/completion.js.map +1 -1
- package/dist/core/tasks/contracts.d.ts +6 -1
- package/dist/core/tasks/contracts.d.ts.map +1 -1
- package/dist/core/tasks/contracts.js.map +1 -1
- package/dist/core/tasks/pause.d.ts +2 -1
- package/dist/core/tasks/pause.d.ts.map +1 -1
- package/dist/core/tasks/pause.js +3 -2
- package/dist/core/tasks/pause.js.map +1 -1
- package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
- package/dist/core/tools/bash-pty-native.js +10 -5
- package/dist/core/tools/bash-pty-native.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +16 -32
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/grep.js +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/kill.d.ts.map +1 -1
- package/dist/core/tools/kill.js +17 -3
- package/dist/core/tools/kill.js.map +1 -1
- package/dist/core/tools/shell-task-render.d.ts +55 -0
- package/dist/core/tools/shell-task-render.d.ts.map +1 -0
- package/dist/core/tools/shell-task-render.js +123 -0
- package/dist/core/tools/shell-task-render.js.map +1 -0
- package/dist/core/tools/shell-task-status.d.ts +52 -0
- package/dist/core/tools/shell-task-status.d.ts.map +1 -0
- package/dist/core/tools/shell-task-status.js +184 -0
- package/dist/core/tools/shell-task-status.js.map +1 -0
- package/dist/core/tools/structured-output.d.ts +5 -0
- package/dist/core/tools/structured-output.d.ts.map +1 -1
- package/dist/core/tools/structured-output.js +3 -0
- package/dist/core/tools/structured-output.js.map +1 -1
- package/dist/extensions/codemode/execute.d.ts.map +1 -1
- package/dist/extensions/codemode/execute.js +134 -57
- package/dist/extensions/codemode/execute.js.map +1 -1
- package/dist/extensions/mcp/config.d.ts.map +1 -1
- package/dist/extensions/mcp/config.js +3 -2
- package/dist/extensions/mcp/config.js.map +1 -1
- package/dist/index-extensions.d.ts +1 -0
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js +3 -0
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/task-row.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-row.js +11 -1
- package/dist/modes/interactive/components/task-row.js.map +1 -1
- package/dist/modes/interactive/interactive-auth-routing.js +12 -10
- package/dist/modes/interactive/interactive-auth-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-custom-ui.js +20 -30
- package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-dialogs.js +23 -96
- package/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-runtime.js +7 -8
- package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +27 -4
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +20 -10
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +40 -5
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -4
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.d.ts +4 -1
- package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
- package/dist/modes/interactive/interactive-prompt-restore.d.ts +3 -0
- package/dist/modes/interactive/interactive-prompt-restore.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-prompt-restore.js +9 -1
- package/dist/modes/interactive/interactive-prompt-restore.js.map +1 -1
- package/dist/modes/interactive/interactive-prompt-turn.js +30 -2
- package/dist/modes/interactive/interactive-prompt-turn.js.map +1 -1
- package/dist/modes/interactive/interactive-queueing.js +43 -8
- package/dist/modes/interactive/interactive-queueing.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +12 -3
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/theme/color-utils.d.ts +6 -1
- package/dist/modes/interactive/theme/color-utils.d.ts.map +1 -1
- package/dist/modes/interactive/theme/color-utils.js +39 -17
- package/dist/modes/interactive/theme/color-utils.js.map +1 -1
- package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-class.js +2 -2
- package/dist/modes/interactive/theme/theme-class.js.map +1 -1
- package/dist/modes/interactive/theme/theme-tokens.d.ts +6 -0
- package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-tokens.js +7 -3
- package/dist/modes/interactive/theme/theme-tokens.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-auth.d.ts +3 -0
- package/dist/modes/interactive-engine/isolated-auth.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-auth.js +13 -1
- package/dist/modes/interactive-engine/isolated-auth.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +11 -2
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.d.ts +8 -0
- package/dist/modes/interactive-engine/remote-model-catalog.d.ts.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.js +54 -0
- package/dist/modes/interactive-engine/remote-model-catalog.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +4 -13
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-model-catalog.d.ts +9 -0
- package/dist/modes/rpc/rpc-model-catalog.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-model-catalog.js +19 -0
- package/dist/modes/rpc/rpc-model-catalog.js.map +1 -0
- package/dist/modes/rpc/rpc-provider-auth.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-provider-auth.js +2 -6
- package/dist/modes/rpc/rpc-provider-auth.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +6 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/changelog.d.ts +6 -0
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +11 -0
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +4 -0
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +3 -2
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/background-tasks.md +5 -3
- package/docs/codemode.md +2 -2
- package/docs/custom-provider/oauth.md +42 -1
- package/docs/custom-provider/registration.md +2 -0
- package/docs/environment-variables.md +1 -0
- package/docs/extensions/api-reference.md +3 -4
- package/docs/extensions/authoring.md +1 -1
- package/docs/extensions/events.md +1 -1
- package/docs/models-configuration.md +9 -0
- package/docs/models.md +3 -1
- package/docs/providers.md +4 -4
- package/docs/rpc/protocol.md +4 -2
- package/docs/sdk/reference.md +2 -0
- package/docs/sdk/structured-decisions.md +7 -5
- package/docs/sdk.md +5 -2
- package/docs/settings.md +4 -2
- package/docs/subagents/reference.md +1 -1
- package/docs/subagents.md +15 -1
- package/docs/themes/reference.md +3 -1
- package/docs/usage.md +3 -1
- package/docs/workflows/api-reference.md +28 -0
- package/docs/workflows/authoring.md +1 -1
- package/docs/workflows/operations.md +2 -2
- package/npm-shrinkwrap.json +67 -54
- package/package.json +6 -6
- package/schemas/models.schema.json +21 -0
- package/dist/core/provider-attribution.d.ts +0 -4
- package/dist/core/provider-attribution.d.ts.map +0 -1
- package/dist/core/provider-attribution.js +0 -87
- package/dist/core/provider-attribution.js.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { OAUTH_REFRESH_LOCK_HOLD_MS, OAUTH_REFRESH_TIMEOUT_MS } from "@bastani/pi-ai";
|
|
2
3
|
import { chmodSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync, } from "fs";
|
|
3
4
|
import { dirname, join } from "path";
|
|
4
|
-
import lockfile from "proper-lockfile";
|
|
5
|
+
import lockfile, {} from "proper-lockfile";
|
|
5
6
|
import { getAgentDir } from "../config.js";
|
|
6
7
|
import { normalizePath } from "../utils/paths.js";
|
|
7
8
|
import { stripBom } from "../utils/text.js";
|
|
@@ -14,6 +15,49 @@ const AUTH_DELETE_LOCK_RETRY_OPTIONS = {
|
|
|
14
15
|
maxTimeout: 2_000,
|
|
15
16
|
randomize: true,
|
|
16
17
|
};
|
|
18
|
+
/** How often a provider lock holder refreshes the lock's heartbeat (its mtime). */
|
|
19
|
+
const PROVIDER_LOCK_HEARTBEAT_MS = 2_000;
|
|
20
|
+
/**
|
|
21
|
+
* Another process takes over a provider lock only once the holder has missed heartbeats for this long:
|
|
22
|
+
* a whole OAuth refresh plus the same again of event-loop lag. A waiter therefore never exchanges a
|
|
23
|
+
* refresh token that a live, merely slow holder is still rotating.
|
|
24
|
+
*/
|
|
25
|
+
const PROVIDER_LOCK_STALE_MS = 2 * OAUTH_REFRESH_TIMEOUT_MS + PROVIDER_LOCK_HEARTBEAT_MS;
|
|
26
|
+
/** How long a commit waits for the shared auth file lock. */
|
|
27
|
+
const AUTH_FILE_LOCK_WAIT_MS = 30_000;
|
|
28
|
+
/**
|
|
29
|
+
* How long a session waits for a provider lock another session holds. A live holder keeps it while its token
|
|
30
|
+
* exchange runs (up to OAUTH_REFRESH_LOCK_HOLD_MS) and then waits for the auth file lock to commit; a dead one
|
|
31
|
+
* loses it after PROVIDER_LOCK_STALE_MS. Waiting out both means a waiter gets the rotated token instead of an error.
|
|
32
|
+
*/
|
|
33
|
+
const PROVIDER_LOCK_WAIT_MS = Math.max(OAUTH_REFRESH_LOCK_HOLD_MS, PROVIDER_LOCK_STALE_MS) + AUTH_FILE_LOCK_WAIT_MS;
|
|
34
|
+
/** A waiter polls a held provider lock at growing intervals, at least once per holder heartbeat (plus jitter). */
|
|
35
|
+
const PROVIDER_LOCK_POLL_MIN_MS = 100;
|
|
36
|
+
const PROVIDER_LOCK_POLL_MAX_MS = PROVIDER_LOCK_HEARTBEAT_MS;
|
|
37
|
+
/** The result of a provider transaction may be a completed token rotation, so a lost or busy auth file lock retries its commit. */
|
|
38
|
+
const PROVIDER_COMMIT_ATTEMPTS = 3;
|
|
39
|
+
/** Waits `ms`, or rejects with the signal's reason as soon as it aborts. */
|
|
40
|
+
function delay(ms, signal) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
if (signal?.aborted) {
|
|
43
|
+
reject(signal.reason);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const onAbort = () => {
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
reject(signal?.reason);
|
|
49
|
+
};
|
|
50
|
+
const timer = setTimeout(() => {
|
|
51
|
+
signal?.removeEventListener("abort", onAbort);
|
|
52
|
+
resolve();
|
|
53
|
+
}, ms);
|
|
54
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function isLockContention(error) {
|
|
58
|
+
const code = error instanceof Error && "code" in error ? error.code : undefined;
|
|
59
|
+
return code === "ECOMPROMISED" || code === "ELOCKED";
|
|
60
|
+
}
|
|
17
61
|
function describeFileVersion(path) {
|
|
18
62
|
try {
|
|
19
63
|
const stats = statSync(path, { bigint: true, throwIfNoEntry: false });
|
|
@@ -33,14 +77,14 @@ export class FileAuthStorageBackend {
|
|
|
33
77
|
this.ensureFileExists();
|
|
34
78
|
const providerPath = `${realpathSync(this.authPath)}.${createHash("sha256").update(provider).digest("hex")}`;
|
|
35
79
|
let compromised;
|
|
36
|
-
const release = await
|
|
80
|
+
const release = await this.acquireProviderLock(providerPath, {
|
|
37
81
|
realpath: false,
|
|
38
|
-
|
|
39
|
-
|
|
82
|
+
stale: PROVIDER_LOCK_STALE_MS,
|
|
83
|
+
update: PROVIDER_LOCK_HEARTBEAT_MS,
|
|
40
84
|
onCompromised: (error) => {
|
|
41
85
|
compromised = error;
|
|
42
86
|
},
|
|
43
|
-
});
|
|
87
|
+
}, signal);
|
|
44
88
|
try {
|
|
45
89
|
signal?.throwIfAborted();
|
|
46
90
|
if (compromised)
|
|
@@ -48,12 +92,10 @@ export class FileAuthStorageBackend {
|
|
|
48
92
|
const originalData = JSON.parse(this.readMergedAuth() ?? "{}");
|
|
49
93
|
const original = JSON.stringify(originalData[provider]);
|
|
50
94
|
const next = await fn(originalData[provider]);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
95
|
+
// Even when another process took over the provider lock meanwhile, `next` may be a token rotation the
|
|
96
|
+
// provider already completed. It is committed only if the stored credential is still the one it replaces.
|
|
97
|
+
const commit = async (content) => {
|
|
54
98
|
signal?.throwIfAborted();
|
|
55
|
-
if (compromised)
|
|
56
|
-
throw compromised;
|
|
57
99
|
const current = JSON.parse(content ?? "{}");
|
|
58
100
|
if (next !== undefined && JSON.stringify(current[provider]) === original) {
|
|
59
101
|
const merged = JSON.stringify({ ...current, [provider]: next }, null, 2);
|
|
@@ -65,7 +107,16 @@ export class FileAuthStorageBackend {
|
|
|
65
107
|
next: content,
|
|
66
108
|
},
|
|
67
109
|
};
|
|
68
|
-
}
|
|
110
|
+
};
|
|
111
|
+
for (let attempt = 1;; attempt++) {
|
|
112
|
+
try {
|
|
113
|
+
return await this.withLockAsync(commit);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (attempt >= PROVIDER_COMMIT_ATTEMPTS || !isLockContention(error))
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
69
120
|
}
|
|
70
121
|
finally {
|
|
71
122
|
await release().catch(() => undefined);
|
|
@@ -91,6 +142,22 @@ export class FileAuthStorageBackend {
|
|
|
91
142
|
return code;
|
|
92
143
|
return undefined;
|
|
93
144
|
}
|
|
145
|
+
/** Takes a provider lock, waiting up to PROVIDER_LOCK_WAIT_MS for its holder; the caller's signal stops the wait. */
|
|
146
|
+
async acquireProviderLock(path, options, signal) {
|
|
147
|
+
const deadline = Date.now() + PROVIDER_LOCK_WAIT_MS;
|
|
148
|
+
for (let pollMs = PROVIDER_LOCK_POLL_MIN_MS;; pollMs = Math.min(pollMs * 2, PROVIDER_LOCK_POLL_MAX_MS)) {
|
|
149
|
+
signal?.throwIfAborted();
|
|
150
|
+
try {
|
|
151
|
+
return await lockfile.lock(path, { ...options, retries: 0 });
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
const remainingMs = deadline - Date.now();
|
|
155
|
+
if (!this.getRetryableLockErrorCode(error) || remainingMs <= 0)
|
|
156
|
+
throw error;
|
|
157
|
+
await delay(Math.min(Math.round(pollMs * (1 + Math.random())), remainingMs), signal);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
94
161
|
acquireLockSyncWithRetry(path) {
|
|
95
162
|
const maxAttempts = 10;
|
|
96
163
|
const delayMs = 20;
|
|
@@ -245,7 +312,7 @@ export class FileAuthStorageBackend {
|
|
|
245
312
|
if (!existsSync(this.authPath)) {
|
|
246
313
|
this.ensureFileExists();
|
|
247
314
|
}
|
|
248
|
-
const deadline = Date.now() +
|
|
315
|
+
const deadline = Date.now() + AUTH_FILE_LOCK_WAIT_MS;
|
|
249
316
|
let retry = 0;
|
|
250
317
|
let epermRetries = 0;
|
|
251
318
|
for (;;) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-storage-backends.js","sourceRoot":"","sources":["../../src/core/auth-storage-backends.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { Credential } from \"@bastani/pi-ai\";\nimport {\n\tchmodSync,\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\trealpathSync,\n\trenameSync,\n\trmSync,\n\tstatSync,\n\twriteFileSync,\n} from \"fs\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.js\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { stripBom } from \"../utils/text.ts\";\nimport type { AuthStorageData } from \"./auth-storage.ts\";\nimport { normalizeProviderKeys } from \"./azure-provider-compat.js\";\n\nexport type LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\nconst AUTH_DELETE_LOCK_RETRY_OPTIONS = {\n\tretries: 5,\n\tfactor: 2,\n\tminTimeout: 100,\n\tmaxTimeout: 2_000,\n\trandomize: true,\n} as const;\n\nfunction describeFileVersion(path: string): string {\n\ttry {\n\t\tconst stats = statSync(path, { bigint: true, throwIfNoEntry: false });\n\t\treturn stats ? `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeNs}:${stats.ctimeNs}` : \"absent\";\n\t} catch (error) {\n\t\treturn `unreadable:${error instanceof Error && \"code\" in error ? String(error.code) : \"unknown\"}`;\n\t}\n}\n\nexport interface AuthStorageBackend {\n\t/**\n\t * Read the current credential snapshot WITHOUT acquiring the exclusive write\n\t * lock. Pure reads do not need cross-process write-exclusion: writers replace\n\t * the file atomically (temp file + rename), so a lock-free reader always\n\t * observes a complete previous-or-next snapshot, never a torn one. Keeping\n\t * reads lock-free is what prevents many concurrent sessions from starving each\n\t * other on `auth.json` and misreporting configured providers as unreadable\n\t * under contention (issue #1431).\n\t *\n\t * Optional for backward compatibility with custom backends that predate this\n\t * method (the released `AuthStorageBackend` interface): when absent,\n\t * `AuthStorage.reload()` falls back to a `withLock`-based read.\n\t */\n\tread?(): string | undefined;\n\tversion?(): string;\n\tdeleteProvider?(provider: string): string | undefined;\n\tdeleteProviderAsync?(provider: string): Promise<string | undefined>;\n\tmodifyProviderAsync?(\n\t\tprovider: string,\n\t\tfn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\tsignal?: AbortSignal,\n\t): Promise<LockResult<Credential | undefined>>;\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate declare authPath: string;\n\tprivate declare readPaths: string[];\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\"), readPaths: string[] = [authPath]) {\n\t\tthis.authPath = normalizePath(authPath);\n\t\tthis.readPaths = readPaths.map((readPath) => normalizePath(readPath));\n\t}\n\n\tasync modifyProviderAsync(\n\t\tprovider: string,\n\t\tfn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\tsignal?: AbortSignal,\n\t): Promise<LockResult<Credential | undefined>> {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\t\tconst providerPath = `${realpathSync(this.authPath)}.${createHash(\"sha256\").update(provider).digest(\"hex\")}`;\n\t\tlet compromised: Error | undefined;\n\t\tconst release = await lockfile.lock(providerPath, {\n\t\t\trealpath: false,\n\t\t\tretries: { retries: 10, factor: 2, minTimeout: 100, maxTimeout: 10000, randomize: true },\n\t\t\tstale: 30000,\n\t\t\tonCompromised: (error) => {\n\t\t\t\tcompromised = error;\n\t\t\t},\n\t\t});\n\t\ttry {\n\t\t\tsignal?.throwIfAborted();\n\t\t\tif (compromised) throw compromised;\n\t\t\tconst originalData = JSON.parse(this.readMergedAuth() ?? \"{}\") as AuthStorageData;\n\t\t\tconst original = JSON.stringify(originalData[provider]);\n\t\t\tconst next = await fn(originalData[provider]);\n\t\t\tif (compromised) throw compromised;\n\t\t\treturn await this.withLockAsync(async (content) => {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tif (compromised) throw compromised;\n\t\t\t\tconst current = JSON.parse(content ?? \"{}\") as AuthStorageData;\n\t\t\t\tif (next !== undefined && JSON.stringify(current[provider]) === original) {\n\t\t\t\t\tconst merged = JSON.stringify({ ...current, [provider]: next }, null, 2);\n\t\t\t\t\treturn { result: { result: next, next: merged }, next: merged };\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tresult: {\n\t\t\t\t\t\tresult: JSON.stringify(current[provider]) === original ? originalData[provider] : current[provider],\n\t\t\t\t\t\tnext: content,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t});\n\t\t} finally {\n\t\t\tawait release().catch(() => undefined);\n\t\t}\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate getRetryableLockErrorCode(error: unknown): \"ELOCKED\" | \"EPERM\" | undefined {\n\t\tconst code = error instanceof Error && \"code\" in error ? error.code : undefined;\n\t\tif (code === \"ELOCKED\") return code;\n\t\tif (code === \"EPERM\" && process.platform === \"win32\") return code;\n\t\treturn undefined;\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tfor (let attempt = 1; ; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tif (!this.getRetryableLockErrorCode(error) || attempt === maxAttempts) throw error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate readMergedAuth(): string | undefined {\n\t\tlet merged: AuthStorageData = {};\n\t\tlet found = false;\n\t\tfor (let i = this.readPaths.length - 1; i >= 0; i--) {\n\t\t\tconst readPath = this.readPaths[i]!;\n\t\t\tif (!existsSync(readPath)) continue;\n\t\t\tconst parsed = JSON.parse(stripBom(readFileSync(readPath, \"utf-8\"))) as AuthStorageData;\n\t\t\tmerged = { ...merged, ...normalizeProviderKeys(parsed) };\n\t\t\tfound = true;\n\t\t}\n\t\treturn found ? JSON.stringify(merged, null, 2) : undefined;\n\t}\n\n\t/**\n\t * Atomically replace `auth.json` with `content`: write a sibling temp file\n\t * (same directory, so `rename` is a same-filesystem atomic swap), fix its\n\t * permissions, then `rename` it over the target. Lock-free readers therefore\n\t * never observe a half-written file. The temp file is best-effort cleaned up\n\t * if the rename fails.\n\t */\n\tprivate writeAtomic(content: string, path = this.authPath): void {\n\t\tconst dir = dirname(path);\n\t\tconst mode = existsSync(path) ? statSync(path).mode & 0o777 : AUTH_FILE_WRITE_OPTIONS.mode;\n\t\tconst tempPath = join(dir, `.${`auth.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}`}.tmp`);\n\t\ttry {\n\t\t\twriteFileSync(tempPath, content, { ...AUTH_FILE_WRITE_OPTIONS, mode });\n\t\t\t// Creation mode is filtered through umask; restore the exact managed mode\n\t\t\t// before publishing the replacement inode.\n\t\t\tchmodSync(tempPath, mode);\n\t\t\trenameSync(tempPath, path);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tif (existsSync(tempPath)) rmSync(tempPath, { force: true });\n\t\t\t} catch {\n\t\t\t\t// Best-effort cleanup; ignore.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tread(): string | undefined {\n\t\treturn this.readMergedAuth();\n\t}\n\n\tversion(): string {\n\t\treturn this.readPaths.map((path) => describeFileVersion(path)).join(\"|\");\n\t}\n\n\tdeleteProvider(provider: string): string | undefined {\n\t\tconst paths = [...new Set(this.readPaths.filter((path) => existsSync(path)))].sort();\n\t\tconst releases: Array<() => void> = [];\n\t\ttry {\n\t\t\tfor (const path of paths) releases.push(this.acquireLockSyncWithRetry(path));\n\t\t\tfor (const path of paths) {\n\t\t\t\tconst data = JSON.parse(stripBom(readFileSync(path, \"utf-8\"))) as AuthStorageData;\n\t\t\t\tif (!(provider in data)) continue;\n\t\t\t\tdelete data[provider];\n\t\t\t\tthis.writeAtomic(JSON.stringify(data, null, 2), path);\n\t\t\t}\n\t\t\treturn this.readMergedAuth();\n\t\t} finally {\n\t\t\tfor (const release of releases.reverse()) release();\n\t\t}\n\t}\n\n\tasync deleteProviderAsync(provider: string): Promise<string | undefined> {\n\t\tconst paths = [...new Set(this.readPaths.filter((path) => existsSync(path)))].sort();\n\t\tconst releases: Array<() => Promise<void>> = [];\n\t\ttry {\n\t\t\tfor (const path of paths) {\n\t\t\t\treleases.push(\n\t\t\t\t\tawait lockfile.lock(path, {\n\t\t\t\t\t\trealpath: false,\n\t\t\t\t\t\t// Keep layered-file deletion below the logout RPC's 30-second\n\t\t\t\t\t\t// deadline even if every auth path is actively locked. A caller\n\t\t\t\t\t\t// then receives the actionable lock error instead of an abandoned\n\t\t\t\t\t\t// request that poisons the engine's ordinary command lane.\n\t\t\t\t\t\tretries: AUTH_DELETE_LOCK_RETRY_OPTIONS,\n\t\t\t\t\t\tstale: 30000,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (const path of paths) {\n\t\t\t\tconst data = JSON.parse(stripBom(readFileSync(path, \"utf-8\"))) as AuthStorageData;\n\t\t\t\tif (!(provider in data)) continue;\n\t\t\t\tdelete data[provider];\n\t\t\t\tthis.writeAtomic(JSON.stringify(data, null, 2), path);\n\t\t\t}\n\t\t\treturn this.readMergedAuth();\n\t\t} finally {\n\t\t\tfor (const release of releases.reverse()) await release();\n\t\t}\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\tif (existsSync(this.authPath)) {\n\t\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\t}\n\t\t\tconst current = this.readMergedAuth();\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\tif (!existsSync(this.authPath)) {\n\t\t\t\t\tthis.ensureFileExists();\n\t\t\t\t}\n\t\t\t\tif (!release) {\n\t\t\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\t\t}\n\t\t\t\tthis.writeAtomic(next);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tthis.ensureParentDir();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\tif (!existsSync(this.authPath)) {\n\t\t\t\tthis.ensureFileExists();\n\t\t\t}\n\t\t\tconst deadline = Date.now() + 30_000;\n\t\t\tlet retry = 0;\n\t\t\tlet epermRetries = 0;\n\t\t\tfor (;;) {\n\t\t\t\ttry {\n\t\t\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\t\t\tretries: 0,\n\t\t\t\t\t\tstale: 30_000,\n\t\t\t\t\t\tonCompromised: (err) => {\n\t\t\t\t\t\t\tlockCompromised = true;\n\t\t\t\t\t\t\tlockCompromisedError = err;\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst code = this.getRetryableLockErrorCode(error);\n\t\t\t\t\tconst remainingMs = deadline - Date.now();\n\t\t\t\t\tif (!code || remainingMs <= 0) throw error;\n\t\t\t\t\tif (code === \"EPERM\" && ++epermRetries > 5) throw error;\n\t\t\t\t\tconst baseDelayMs = Math.min(10 * 2 ** retry++, 1_000);\n\t\t\t\t\tconst delayMs = Math.min(Math.round(baseDelayMs * (1 + Math.random())), remainingMs);\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delayMs));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrowIfCompromised();\n\t\t\tconst current = this.readMergedAuth();\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\tif (next !== undefined) {\n\t\t\t\tthis.writeAtomic(next);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\tprivate pendingWrite: Promise<void> = Promise.resolve();\n\n\tread(): string | undefined {\n\t\treturn this.value;\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tconst previous = this.pendingWrite;\n\t\tlet release!: () => void;\n\t\tthis.pendingWrite = new Promise<void>((resolve) => {\n\t\t\trelease = resolve;\n\t\t});\n\t\tawait previous;\n\t\ttry {\n\t\t\tconst { result, next } = await fn(this.value);\n\t\t\tif (next !== undefined) {\n\t\t\t\tthis.value = next;\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\trelease();\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth-storage-backends.js","sourceRoot":"","sources":["../../src/core/auth-storage-backends.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { type Credential, OAUTH_REFRESH_LOCK_HOLD_MS, OAUTH_REFRESH_TIMEOUT_MS } from \"@bastani/pi-ai\";\nimport {\n\tchmodSync,\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\trealpathSync,\n\trenameSync,\n\trmSync,\n\tstatSync,\n\twriteFileSync,\n} from \"fs\";\nimport { dirname, join } from \"path\";\nimport lockfile, { type LockOptions } from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.js\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { stripBom } from \"../utils/text.ts\";\nimport type { AuthStorageData } from \"./auth-storage.ts\";\nimport { normalizeProviderKeys } from \"./azure-provider-compat.js\";\n\nexport type LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\nconst AUTH_DELETE_LOCK_RETRY_OPTIONS = {\n\tretries: 5,\n\tfactor: 2,\n\tminTimeout: 100,\n\tmaxTimeout: 2_000,\n\trandomize: true,\n} as const;\n\n/** How often a provider lock holder refreshes the lock's heartbeat (its mtime). */\nconst PROVIDER_LOCK_HEARTBEAT_MS = 2_000;\n\n/**\n * Another process takes over a provider lock only once the holder has missed heartbeats for this long:\n * a whole OAuth refresh plus the same again of event-loop lag. A waiter therefore never exchanges a\n * refresh token that a live, merely slow holder is still rotating.\n */\nconst PROVIDER_LOCK_STALE_MS = 2 * OAUTH_REFRESH_TIMEOUT_MS + PROVIDER_LOCK_HEARTBEAT_MS;\n\n/** How long a commit waits for the shared auth file lock. */\nconst AUTH_FILE_LOCK_WAIT_MS = 30_000;\n\n/**\n * How long a session waits for a provider lock another session holds. A live holder keeps it while its token\n * exchange runs (up to OAUTH_REFRESH_LOCK_HOLD_MS) and then waits for the auth file lock to commit; a dead one\n * loses it after PROVIDER_LOCK_STALE_MS. Waiting out both means a waiter gets the rotated token instead of an error.\n */\nconst PROVIDER_LOCK_WAIT_MS = Math.max(OAUTH_REFRESH_LOCK_HOLD_MS, PROVIDER_LOCK_STALE_MS) + AUTH_FILE_LOCK_WAIT_MS;\n\n/** A waiter polls a held provider lock at growing intervals, at least once per holder heartbeat (plus jitter). */\nconst PROVIDER_LOCK_POLL_MIN_MS = 100;\nconst PROVIDER_LOCK_POLL_MAX_MS = PROVIDER_LOCK_HEARTBEAT_MS;\n\n/** The result of a provider transaction may be a completed token rotation, so a lost or busy auth file lock retries its commit. */\nconst PROVIDER_COMMIT_ATTEMPTS = 3;\n\n/** Waits `ms`, or rejects with the signal's reason as soon as it aborts. */\nfunction delay(ms: number, signal: AbortSignal | undefined): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(signal.reason);\n\t\t\treturn;\n\t\t}\n\t\tconst onAbort = () => {\n\t\t\tclearTimeout(timer);\n\t\t\treject(signal?.reason);\n\t\t};\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tresolve();\n\t\t}, ms);\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n}\n\nfunction isLockContention(error: unknown): boolean {\n\tconst code = error instanceof Error && \"code\" in error ? error.code : undefined;\n\treturn code === \"ECOMPROMISED\" || code === \"ELOCKED\";\n}\n\nfunction describeFileVersion(path: string): string {\n\ttry {\n\t\tconst stats = statSync(path, { bigint: true, throwIfNoEntry: false });\n\t\treturn stats ? `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeNs}:${stats.ctimeNs}` : \"absent\";\n\t} catch (error) {\n\t\treturn `unreadable:${error instanceof Error && \"code\" in error ? String(error.code) : \"unknown\"}`;\n\t}\n}\n\nexport interface AuthStorageBackend {\n\t/**\n\t * Read the current credential snapshot WITHOUT acquiring the exclusive write\n\t * lock. Pure reads do not need cross-process write-exclusion: writers replace\n\t * the file atomically (temp file + rename), so a lock-free reader always\n\t * observes a complete previous-or-next snapshot, never a torn one. Keeping\n\t * reads lock-free is what prevents many concurrent sessions from starving each\n\t * other on `auth.json` and misreporting configured providers as unreadable\n\t * under contention (issue #1431).\n\t *\n\t * Optional for backward compatibility with custom backends that predate this\n\t * method (the released `AuthStorageBackend` interface): when absent,\n\t * `AuthStorage.reload()` falls back to a `withLock`-based read.\n\t */\n\tread?(): string | undefined;\n\tversion?(): string;\n\tdeleteProvider?(provider: string): string | undefined;\n\tdeleteProviderAsync?(provider: string): Promise<string | undefined>;\n\tmodifyProviderAsync?(\n\t\tprovider: string,\n\t\tfn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\tsignal?: AbortSignal,\n\t): Promise<LockResult<Credential | undefined>>;\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate declare authPath: string;\n\tprivate declare readPaths: string[];\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\"), readPaths: string[] = [authPath]) {\n\t\tthis.authPath = normalizePath(authPath);\n\t\tthis.readPaths = readPaths.map((readPath) => normalizePath(readPath));\n\t}\n\n\tasync modifyProviderAsync(\n\t\tprovider: string,\n\t\tfn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\tsignal?: AbortSignal,\n\t): Promise<LockResult<Credential | undefined>> {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\t\tconst providerPath = `${realpathSync(this.authPath)}.${createHash(\"sha256\").update(provider).digest(\"hex\")}`;\n\t\tlet compromised: Error | undefined;\n\t\tconst release = await this.acquireProviderLock(\n\t\t\tproviderPath,\n\t\t\t{\n\t\t\t\trealpath: false,\n\t\t\t\tstale: PROVIDER_LOCK_STALE_MS,\n\t\t\t\tupdate: PROVIDER_LOCK_HEARTBEAT_MS,\n\t\t\t\tonCompromised: (error) => {\n\t\t\t\t\tcompromised = error;\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\ttry {\n\t\t\tsignal?.throwIfAborted();\n\t\t\tif (compromised) throw compromised;\n\t\t\tconst originalData = JSON.parse(this.readMergedAuth() ?? \"{}\") as AuthStorageData;\n\t\t\tconst original = JSON.stringify(originalData[provider]);\n\t\t\tconst next = await fn(originalData[provider]);\n\t\t\t// Even when another process took over the provider lock meanwhile, `next` may be a token rotation the\n\t\t\t// provider already completed. It is committed only if the stored credential is still the one it replaces.\n\t\t\tconst commit = async (\n\t\t\t\tcontent: string | undefined,\n\t\t\t): Promise<LockResult<LockResult<Credential | undefined>>> => {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tconst current = JSON.parse(content ?? \"{}\") as AuthStorageData;\n\t\t\t\tif (next !== undefined && JSON.stringify(current[provider]) === original) {\n\t\t\t\t\tconst merged = JSON.stringify({ ...current, [provider]: next }, null, 2);\n\t\t\t\t\treturn { result: { result: next, next: merged }, next: merged };\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tresult: {\n\t\t\t\t\t\tresult: JSON.stringify(current[provider]) === original ? originalData[provider] : current[provider],\n\t\t\t\t\t\tnext: content,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t};\n\t\t\tfor (let attempt = 1; ; attempt++) {\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.withLockAsync(commit);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (attempt >= PROVIDER_COMMIT_ATTEMPTS || !isLockContention(error)) throw error;\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tawait release().catch(() => undefined);\n\t\t}\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate getRetryableLockErrorCode(error: unknown): \"ELOCKED\" | \"EPERM\" | undefined {\n\t\tconst code = error instanceof Error && \"code\" in error ? error.code : undefined;\n\t\tif (code === \"ELOCKED\") return code;\n\t\tif (code === \"EPERM\" && process.platform === \"win32\") return code;\n\t\treturn undefined;\n\t}\n\n\t/** Takes a provider lock, waiting up to PROVIDER_LOCK_WAIT_MS for its holder; the caller's signal stops the wait. */\n\tprivate async acquireProviderLock(\n\t\tpath: string,\n\t\toptions: Omit<LockOptions, \"retries\">,\n\t\tsignal: AbortSignal | undefined,\n\t): Promise<() => Promise<void>> {\n\t\tconst deadline = Date.now() + PROVIDER_LOCK_WAIT_MS;\n\t\tfor (let pollMs = PROVIDER_LOCK_POLL_MIN_MS; ; pollMs = Math.min(pollMs * 2, PROVIDER_LOCK_POLL_MAX_MS)) {\n\t\t\tsignal?.throwIfAborted();\n\t\t\ttry {\n\t\t\t\treturn await lockfile.lock(path, { ...options, retries: 0 });\n\t\t\t} catch (error) {\n\t\t\t\tconst remainingMs = deadline - Date.now();\n\t\t\t\tif (!this.getRetryableLockErrorCode(error) || remainingMs <= 0) throw error;\n\t\t\t\tawait delay(Math.min(Math.round(pollMs * (1 + Math.random())), remainingMs), signal);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tfor (let attempt = 1; ; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tif (!this.getRetryableLockErrorCode(error) || attempt === maxAttempts) throw error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate readMergedAuth(): string | undefined {\n\t\tlet merged: AuthStorageData = {};\n\t\tlet found = false;\n\t\tfor (let i = this.readPaths.length - 1; i >= 0; i--) {\n\t\t\tconst readPath = this.readPaths[i]!;\n\t\t\tif (!existsSync(readPath)) continue;\n\t\t\tconst parsed = JSON.parse(stripBom(readFileSync(readPath, \"utf-8\"))) as AuthStorageData;\n\t\t\tmerged = { ...merged, ...normalizeProviderKeys(parsed) };\n\t\t\tfound = true;\n\t\t}\n\t\treturn found ? JSON.stringify(merged, null, 2) : undefined;\n\t}\n\n\t/**\n\t * Atomically replace `auth.json` with `content`: write a sibling temp file\n\t * (same directory, so `rename` is a same-filesystem atomic swap), fix its\n\t * permissions, then `rename` it over the target. Lock-free readers therefore\n\t * never observe a half-written file. The temp file is best-effort cleaned up\n\t * if the rename fails.\n\t */\n\tprivate writeAtomic(content: string, path = this.authPath): void {\n\t\tconst dir = dirname(path);\n\t\tconst mode = existsSync(path) ? statSync(path).mode & 0o777 : AUTH_FILE_WRITE_OPTIONS.mode;\n\t\tconst tempPath = join(dir, `.${`auth.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}`}.tmp`);\n\t\ttry {\n\t\t\twriteFileSync(tempPath, content, { ...AUTH_FILE_WRITE_OPTIONS, mode });\n\t\t\t// Creation mode is filtered through umask; restore the exact managed mode\n\t\t\t// before publishing the replacement inode.\n\t\t\tchmodSync(tempPath, mode);\n\t\t\trenameSync(tempPath, path);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tif (existsSync(tempPath)) rmSync(tempPath, { force: true });\n\t\t\t} catch {\n\t\t\t\t// Best-effort cleanup; ignore.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tread(): string | undefined {\n\t\treturn this.readMergedAuth();\n\t}\n\n\tversion(): string {\n\t\treturn this.readPaths.map((path) => describeFileVersion(path)).join(\"|\");\n\t}\n\n\tdeleteProvider(provider: string): string | undefined {\n\t\tconst paths = [...new Set(this.readPaths.filter((path) => existsSync(path)))].sort();\n\t\tconst releases: Array<() => void> = [];\n\t\ttry {\n\t\t\tfor (const path of paths) releases.push(this.acquireLockSyncWithRetry(path));\n\t\t\tfor (const path of paths) {\n\t\t\t\tconst data = JSON.parse(stripBom(readFileSync(path, \"utf-8\"))) as AuthStorageData;\n\t\t\t\tif (!(provider in data)) continue;\n\t\t\t\tdelete data[provider];\n\t\t\t\tthis.writeAtomic(JSON.stringify(data, null, 2), path);\n\t\t\t}\n\t\t\treturn this.readMergedAuth();\n\t\t} finally {\n\t\t\tfor (const release of releases.reverse()) release();\n\t\t}\n\t}\n\n\tasync deleteProviderAsync(provider: string): Promise<string | undefined> {\n\t\tconst paths = [...new Set(this.readPaths.filter((path) => existsSync(path)))].sort();\n\t\tconst releases: Array<() => Promise<void>> = [];\n\t\ttry {\n\t\t\tfor (const path of paths) {\n\t\t\t\treleases.push(\n\t\t\t\t\tawait lockfile.lock(path, {\n\t\t\t\t\t\trealpath: false,\n\t\t\t\t\t\t// Keep layered-file deletion below the logout RPC's 30-second\n\t\t\t\t\t\t// deadline even if every auth path is actively locked. A caller\n\t\t\t\t\t\t// then receives the actionable lock error instead of an abandoned\n\t\t\t\t\t\t// request that poisons the engine's ordinary command lane.\n\t\t\t\t\t\tretries: AUTH_DELETE_LOCK_RETRY_OPTIONS,\n\t\t\t\t\t\tstale: 30000,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (const path of paths) {\n\t\t\t\tconst data = JSON.parse(stripBom(readFileSync(path, \"utf-8\"))) as AuthStorageData;\n\t\t\t\tif (!(provider in data)) continue;\n\t\t\t\tdelete data[provider];\n\t\t\t\tthis.writeAtomic(JSON.stringify(data, null, 2), path);\n\t\t\t}\n\t\t\treturn this.readMergedAuth();\n\t\t} finally {\n\t\t\tfor (const release of releases.reverse()) await release();\n\t\t}\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\tif (existsSync(this.authPath)) {\n\t\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\t}\n\t\t\tconst current = this.readMergedAuth();\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\tif (!existsSync(this.authPath)) {\n\t\t\t\t\tthis.ensureFileExists();\n\t\t\t\t}\n\t\t\t\tif (!release) {\n\t\t\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\t\t}\n\t\t\t\tthis.writeAtomic(next);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tthis.ensureParentDir();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\tif (!existsSync(this.authPath)) {\n\t\t\t\tthis.ensureFileExists();\n\t\t\t}\n\t\t\tconst deadline = Date.now() + AUTH_FILE_LOCK_WAIT_MS;\n\t\t\tlet retry = 0;\n\t\t\tlet epermRetries = 0;\n\t\t\tfor (;;) {\n\t\t\t\ttry {\n\t\t\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\t\t\tretries: 0,\n\t\t\t\t\t\tstale: 30_000,\n\t\t\t\t\t\tonCompromised: (err) => {\n\t\t\t\t\t\t\tlockCompromised = true;\n\t\t\t\t\t\t\tlockCompromisedError = err;\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst code = this.getRetryableLockErrorCode(error);\n\t\t\t\t\tconst remainingMs = deadline - Date.now();\n\t\t\t\t\tif (!code || remainingMs <= 0) throw error;\n\t\t\t\t\tif (code === \"EPERM\" && ++epermRetries > 5) throw error;\n\t\t\t\t\tconst baseDelayMs = Math.min(10 * 2 ** retry++, 1_000);\n\t\t\t\t\tconst delayMs = Math.min(Math.round(baseDelayMs * (1 + Math.random())), remainingMs);\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delayMs));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrowIfCompromised();\n\t\t\tconst current = this.readMergedAuth();\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\tif (next !== undefined) {\n\t\t\t\tthis.writeAtomic(next);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\tprivate pendingWrite: Promise<void> = Promise.resolve();\n\n\tread(): string | undefined {\n\t\treturn this.value;\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tconst previous = this.pendingWrite;\n\t\tlet release!: () => void;\n\t\tthis.pendingWrite = new Promise<void>((resolve) => {\n\t\t\trelease = resolve;\n\t\t});\n\t\tawait previous;\n\t\ttry {\n\t\t\tconst { result, next } = await fn(this.value);\n\t\t\tif (next !== undefined) {\n\t\t\t\tthis.value = next;\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\trelease();\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ClientIdentity } from "@bastani/pi-ai";
|
|
2
|
+
import type { SettingsManager } from "./settings-manager.ts";
|
|
3
|
+
/**
|
|
4
|
+
* How Atomic's requests identify it to providers: its own detailed `User-Agent`, and attribution to Atomic as a CLI
|
|
5
|
+
* agent while install telemetry is enabled.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getClientIdentity(settingsManager: SettingsManager): ClientIdentity;
|
|
8
|
+
//# sourceMappingURL=client-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-identity.d.ts","sourceRoot":"","sources":["../../src/core/client-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM7D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,eAAe,GAAG,cAAc,CAKlF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { APP_NAME, VERSION } from "../config.js";
|
|
2
|
+
import { getPiUserAgent } from "../utils/pi-user-agent.js";
|
|
3
|
+
import { isInstallTelemetryEnabled } from "./telemetry.js";
|
|
4
|
+
/** Who provider requests are attributed to: OpenRouter's app title and referer, NVIDIA's invoke origin, OpenCode's client. */
|
|
5
|
+
const ATOMIC_ATTRIBUTION = { name: APP_NAME, url: "https://atomic.sh", categories: ["cli-agent"] };
|
|
6
|
+
/**
|
|
7
|
+
* How Atomic's requests identify it to providers: its own detailed `User-Agent`, and attribution to Atomic as a CLI
|
|
8
|
+
* agent while install telemetry is enabled.
|
|
9
|
+
*/
|
|
10
|
+
export function getClientIdentity(settingsManager) {
|
|
11
|
+
return {
|
|
12
|
+
userAgent: { custom: getPiUserAgent(VERSION) },
|
|
13
|
+
attribution: isInstallTelemetryEnabled(settingsManager) ? ATOMIC_ATTRIBUTION : null,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=client-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-identity.js","sourceRoot":"","sources":["../../src/core/client-identity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,8HAA8H;AAC9H,MAAM,kBAAkB,GAAsB,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,mBAAmB,EAAE,UAAU,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;AAEtH;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,eAAgC;IACjE,OAAO;QACN,SAAS,EAAE,EAAE,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE;QAC9C,WAAW,EAAE,yBAAyB,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI;KACnF,CAAC;AACH,CAAC","sourcesContent":["import type { ClientAttribution, ClientIdentity } from \"@bastani/pi-ai\";\nimport { APP_NAME, VERSION } from \"../config.js\";\nimport { getPiUserAgent } from \"../utils/pi-user-agent.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\nimport { isInstallTelemetryEnabled } from \"./telemetry.ts\";\n\n/** Who provider requests are attributed to: OpenRouter's app title and referer, NVIDIA's invoke origin, OpenCode's client. */\nconst ATOMIC_ATTRIBUTION: ClientAttribution = { name: APP_NAME, url: \"https://atomic.sh\", categories: [\"cli-agent\"] };\n\n/**\n * How Atomic's requests identify it to providers: its own detailed `User-Agent`, and attribution to Atomic as a CLI\n * agent while install telemetry is enabled.\n */\nexport function getClientIdentity(settingsManager: SettingsManager): ClientIdentity {\n\treturn {\n\t\tuserAgent: { custom: getPiUserAgent(VERSION) },\n\t\tattribution: isInstallTelemetryEnabled(settingsManager) ? ATOMIC_ATTRIBUTION : null,\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-outcome.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/planner-outcome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAEhF,sDAAsD;AACtD,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,cAAc,CAAC;AAEzD,6CAA6C;AAC7C,MAAM,MAAM,cAAc,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,YAAY,EAAE,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,YAAY,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACrE;IACA,IAAI,EAAE,aAAa,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACvB,GACD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,yBAAyB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACnG;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,oFAAoF;AACpF,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAC3C,cAAc,EACd;IAAE,IAAI,EAAE,aAAa,GAAG,UAAU,GAAG,YAAY,GAAG,eAAe,GAAG,eAAe,CAAA;CAAE,CACvF,CAAC;AAEF,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,cAAc,GACrB,OAAO,IAAI,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAA;CAAE,CAAC,CAEtE;
|
|
1
|
+
{"version":3,"file":"planner-outcome.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/planner-outcome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAEhF,sDAAsD;AACtD,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,cAAc,CAAC;AAEzD,6CAA6C;AAC7C,MAAM,MAAM,cAAc,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,YAAY,EAAE,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,YAAY,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACrE;IACA,IAAI,EAAE,aAAa,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACvB,GACD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,yBAAyB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACnG;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,oFAAoF;AACpF,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAC3C,cAAc,EACd;IAAE,IAAI,EAAE,aAAa,GAAG,UAAU,GAAG,YAAY,GAAG,eAAe,GAAG,eAAe,CAAA;CAAE,CACvF,CAAC;AAEF,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,cAAc,GACrB,OAAO,IAAI,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAA;CAAE,CAAC,CAEtE;AA8DD,8EAA8E;AAC9E,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,OAAO,GAAG,cAAc,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;AAE9G,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOhE;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAEnF;AACD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,mBAAmB,CAO7G;AAED,6FAA6F;AAC7F,wBAAgB,sBAAsB,CACrC,KAAK,EAAE;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EACjD,YAAY,EAAE,MAAM,GAClB,gBAAgB,CAmBlB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,eAAe,EAAE,OAAO,GAAG,OAAO,CAEhG"}
|
|
@@ -53,7 +53,8 @@ const RATE_LIMITED_PATTERN = buildProviderErrorPattern([
|
|
|
53
53
|
"overloaded",
|
|
54
54
|
"rate.?limit",
|
|
55
55
|
"too many requests",
|
|
56
|
-
|
|
56
|
+
// Not part of a longer word or number, such as a request id or byte count; pi-ai matches it the same way.
|
|
57
|
+
"(?<![a-z0-9])429(?![a-z0-9])",
|
|
57
58
|
"service.?unavailable",
|
|
58
59
|
"server.?error",
|
|
59
60
|
"internal.?error",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-outcome.js","sourceRoot":"","sources":["../../../src/core/compaction/planner-outcome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAoC1D,+DAA+D;AAC/D,MAAM,UAAU,gBAAgB,CAC/B,OAAuB;IAEvB,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC;AAClE,CAAC;AAED,SAAS,yBAAyB,CAAC,QAA2B;IAC7D,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;IACzD,mBAAmB;IACnB,qBAAqB;IACrB,6BAA6B;IAC7B,mBAAmB;IACnB,oBAAoB;IACpB,eAAe;IACf,gBAAgB;IAChB,SAAS;CACT,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,MAAM,yBAAyB,GAAG,eAAe,CAAC;AAElD;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,yBAAyB,CAAC;IACtD,YAAY;IACZ,aAAa;IACb,mBAAmB;IACnB,KAAK;IACL,sBAAsB;IACtB,eAAe;IACf,iBAAiB;IACjB,mBAAmB;CACnB,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,gBAAgB,GAAG,0BAA0B,CAAC;AAKpD,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACtD,OAAO,CACN,mDAAmD,CAAC,IAAI,CAAC,OAAO,CAAC;QACjE,gNAAgN,CAAC,IAAI,CACpN,OAAO,CACP,CACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,+BAA+B,CAAC,QAA0B;IACzE,OAAO,QAAQ,CAAC,aAAa,KAAK,SAAS,IAAI,uBAAuB,CAAC,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;AACrG,CAAC;AACD;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAA0B,EAAE,aAAqB;IACvF,IAAI,+BAA+B,CAAC,QAAQ,CAAC;QAAE,OAAO,gBAAgB,CAAC;IACvE,IAAI,iBAAiB,CAAC,QAAQ,EAAE,aAAa,CAAC;QAAE,OAAO,UAAU,CAAC;IAClE,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC;IAC5C,IAAI,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IACrG,IAAI,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,cAAc,CAAC;IAChG,OAAO,gBAAgB,CAAC;AACzB,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,sBAAsB,CACrC,KAAiD,EACjD,YAAoB;IAEpB,OAAO;QACN,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,EAAE;QACX,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,EAAE;QACf,KAAK,EAAE;YACN,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,CAAC;YACT,SAAS,EAAE,CAAC;YACZ,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,CAAC;YACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;SACpE;QACD,UAAU,EAAE,OAAO;QACnB,YAAY;QACZ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACD,CAAC;AACvB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAA0B,EAAE,eAAwB;IACtF,OAAO,QAAQ,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,eAAe,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AACrG,CAAC","sourcesContent":["/**\n * Typed outcomes for one compaction planner attempt.\n *\n * `planDeletedLineRanges` used to collapse ranked success, silent partial\n * recovery, reasoning starvation, rate limiting, malformed output, and context\n * overflow into \"ranges or throw\". Callers could not tell a throttle from\n * garbage without string-matching an error message. This module makes each\n * outcome a distinct value so the rung ladder, the `0600` diagnostic sidecar,\n * and the UI can separate them.\n *\n * Cancellation is deliberately NOT a variant: an abort still throws.\n */\n\nimport type { Api, AssistantMessage } from \"@bastani/pi-ai/compat\";\nimport { isContextOverflow } from \"@bastani/pi-ai/compat\";\nimport type { RawLineRange } from \"./compaction-types.js\";\nimport type { DiagnosticFailureCategory } from \"./range-planner-diagnostics.js\";\n\n/** Which limit a `rateLimited` outcome represents. */\nexport type PlannerLimitClass = \"quota\" | \"rate_limited\";\n\n/** Every way one planner attempt can end. */\nexport type PlannerOutcome =\n\t| { kind: \"ranked\"; ranges: RawLineRange[] }\n\t| { kind: \"recovered\"; ranges: RawLineRange[]; recoveredCount: number }\n\t| {\n\t\t\tkind: \"rateLimited\";\n\t\t\t/**\n\t\t\t * Which limit this was, independent of retry activity. Quota/billing\n\t\t\t * exhaustion and transient throttling are different facts and must stay\n\t\t\t * separable in code and diagnostics; `exhausted` records only whether a\n\t\t\t * retry was actually scheduled, which cannot tell them apart when retry\n\t\t\t * is disabled.\n\t\t\t */\n\t\t\tcategory: PlannerLimitClass;\n\t\t\texhausted: boolean;\n\t\t\tmessage: string;\n\t\t\tdiagnosticPath?: string;\n\t }\n\t| { kind: \"unusable\"; category: DiagnosticFailureCategory; excerpt: string; diagnosticPath?: string }\n\t| { kind: \"overflowed\"; diagnosticPath?: string }\n\t| { kind: \"policyRefusal\"; message: string; diagnosticPath?: string }\n\t| { kind: \"providerError\"; message: string; diagnosticPath?: string };\n\n/** A planner outcome that cannot produce a boundary and must advance the ladder. */\nexport type TerminalPlannerOutcome = Extract<\n\tPlannerOutcome,\n\t{ kind: \"rateLimited\" | \"unusable\" | \"overflowed\" | \"providerError\" | \"policyRefusal\" }\n>;\n\n/** Whether this outcome produced validated deletion ranges. */\nexport function isPlannerSuccess(\n\toutcome: PlannerOutcome,\n): outcome is Extract<PlannerOutcome, { kind: \"ranked\" | \"recovered\" }> {\n\treturn outcome.kind === \"ranked\" || outcome.kind === \"recovered\";\n}\n\nfunction buildProviderErrorPattern(patterns: readonly string[]): RegExp {\n\treturn new RegExp(patterns.join(\"|\"), \"i\");\n}\n\n/**\n * Quota/billing exhaustion. Mirrors pi-ai's\n * `NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN` (`utils/retry.js`), which takes\n * precedence over the transient patterns and never spends backoff.\n */\nconst QUOTA_EXHAUSTED_PATTERN = buildProviderErrorPattern([\n\t\"GoUsageLimitError\",\n\t\"FreeUsageLimitError\",\n\t\"Monthly usage limit reached\",\n\t\"available balance\",\n\t\"insufficient_quota\",\n\t\"out of budget\",\n\t\"quota exceeded\",\n\t\"billing\",\n]);\n\n/**\n * Generic usage-limit wording, e.g. Codex's \"The usage limit has been reached\".\n *\n * This is a deliberate **local addition**, not a mirror: pi-ai lists it in\n * neither its non-retryable nor its retryable pattern, so `retryAssistantCall`\n * never schedules a backoff for it. Classifying it as transient throttling would\n * report `exhausted: true` for a retry budget that was never spent, so Atomic\n * treats it as quota exhaustion (`exhausted: false`). pi-ai's own\n * `Monthly usage limit reached` entry above already covers the specific form.\n */\nconst LOCAL_USAGE_LIMIT_PATTERN = /usage.?limit/i;\n\n/**\n * Transient throttling and provider-load failures. This is the rate-limit\n * subset of pi-ai's `RETRYABLE_PROVIDER_ERROR_PATTERN`, minus the individual\n * status codes, which `HTTP_5XX_PATTERN` generalizes below.\n */\nconst RATE_LIMITED_PATTERN = buildProviderErrorPattern([\n\t\"overloaded\",\n\t\"rate.?limit\",\n\t\"too many requests\",\n\t\"429\",\n\t\"service.?unavailable\",\n\t\"server.?error\",\n\t\"internal.?error\",\n\t\"ResourceExhausted\",\n]);\n\n/**\n * The whole HTTP 5xx class, bounded so it cannot match inside a longer number.\n *\n * A deliberate local broadening: the RFC and the acceptance contract enumerate\n * `5xx`, while pi-ai lists only 500/502/503/504/524, so a `501` would otherwise\n * fall through to `providerError`. The consequence is that pi-ai may schedule no\n * backoff for a status Atomic types as rate limiting — which is exactly why\n * `exhausted` reports *observed* retry activity rather than assuming it.\n */\nconst HTTP_5XX_PATTERN = /(?<![\\d.])5\\d\\d(?![\\d.])/;\n\n/** How a failed planner response is classified before an outcome is built. */\nexport type PlannerFailureClass = \"overflow\" | \"quota\" | \"rate_limited\" | \"policy_refusal\" | \"provider_error\";\n\nexport function isProviderPolicyRefusal(message: string): boolean {\n\treturn (\n\t\t/^The model refused to complete the request[.!]?$/i.test(message) ||\n\t\t/terms of service|usage[\\s_-]*policy|acceptable use policy|content[\\s_-]*policy|policy violation|violat(?:e|es|ed|ion|ing)[\\s\\S]*policy|blocked[\\s\\S]*(?:policy|reverse engineering|duplicating model outputs)/i.test(\n\t\t\tmessage,\n\t\t)\n\t);\n}\n\nexport function isProviderPolicyRefusalResponse(response: AssistantMessage): boolean {\n\treturn response.rawStopReason === \"refusal\" || isProviderPolicyRefusal(response.errorMessage ?? \"\");\n}\n/**\n * Classify one failed planner response.\n *\n * Policy refusal wins before overflow, quota, and retryable-provider matching.\n * Context overflow is recoverable by trimming; quota wins over throttling.\n */\nexport function classifyPlannerFailure(response: AssistantMessage, contextWindow: number): PlannerFailureClass {\n\tif (isProviderPolicyRefusalResponse(response)) return \"policy_refusal\";\n\tif (isContextOverflow(response, contextWindow)) return \"overflow\";\n\tconst message = response.errorMessage ?? \"\";\n\tif (QUOTA_EXHAUSTED_PATTERN.test(message) || LOCAL_USAGE_LIMIT_PATTERN.test(message)) return \"quota\";\n\tif (RATE_LIMITED_PATTERN.test(message) || HTTP_5XX_PATTERN.test(message)) return \"rate_limited\";\n\treturn \"provider_error\";\n}\n\n/** Build a synthetic error response so a thrown transport failure classifies identically. */\nexport function syntheticErrorResponse(\n\tmodel: { api: Api; provider: string; id: string },\n\terrorMessage: string,\n): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: \"error\",\n\t\terrorMessage,\n\t\ttimestamp: Date.now(),\n\t} as AssistantMessage;\n}\n\n/**\n * Reasoning starvation fingerprint: the response stopped at the output limit,\n * produced no usable deletion record, and billed reasoning tokens.\n *\n * This is a diagnostic category only. Codex has no starvation concept and never\n * retries one; neither does Atomic. A starved outcome advances the ladder\n * exactly like any other `unusable` output.\n */\nexport function isReasoningStarved(response: AssistantMessage, hasUsableRanges: boolean): boolean {\n\treturn response.stopReason === \"length\" && !hasUsableRanges && (response.usage?.reasoning ?? 0) > 0;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"planner-outcome.js","sourceRoot":"","sources":["../../../src/core/compaction/planner-outcome.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Typed outcomes for one compaction planner attempt.\n *\n * `planDeletedLineRanges` used to collapse ranked success, silent partial\n * recovery, reasoning starvation, rate limiting, malformed output, and context\n * overflow into \"ranges or throw\". Callers could not tell a throttle from\n * garbage without string-matching an error message. This module makes each\n * outcome a distinct value so the rung ladder, the `0600` diagnostic sidecar,\n * and the UI can separate them.\n *\n * Cancellation is deliberately NOT a variant: an abort still throws.\n */\n\nimport type { Api, AssistantMessage } from \"@bastani/pi-ai/compat\";\nimport { isContextOverflow } from \"@bastani/pi-ai/compat\";\nimport type { RawLineRange } from \"./compaction-types.js\";\nimport type { DiagnosticFailureCategory } from \"./range-planner-diagnostics.js\";\n\n/** Which limit a `rateLimited` outcome represents. */\nexport type PlannerLimitClass = \"quota\" | \"rate_limited\";\n\n/** Every way one planner attempt can end. */\nexport type PlannerOutcome =\n\t| { kind: \"ranked\"; ranges: RawLineRange[] }\n\t| { kind: \"recovered\"; ranges: RawLineRange[]; recoveredCount: number }\n\t| {\n\t\t\tkind: \"rateLimited\";\n\t\t\t/**\n\t\t\t * Which limit this was, independent of retry activity. Quota/billing\n\t\t\t * exhaustion and transient throttling are different facts and must stay\n\t\t\t * separable in code and diagnostics; `exhausted` records only whether a\n\t\t\t * retry was actually scheduled, which cannot tell them apart when retry\n\t\t\t * is disabled.\n\t\t\t */\n\t\t\tcategory: PlannerLimitClass;\n\t\t\texhausted: boolean;\n\t\t\tmessage: string;\n\t\t\tdiagnosticPath?: string;\n\t }\n\t| { kind: \"unusable\"; category: DiagnosticFailureCategory; excerpt: string; diagnosticPath?: string }\n\t| { kind: \"overflowed\"; diagnosticPath?: string }\n\t| { kind: \"policyRefusal\"; message: string; diagnosticPath?: string }\n\t| { kind: \"providerError\"; message: string; diagnosticPath?: string };\n\n/** A planner outcome that cannot produce a boundary and must advance the ladder. */\nexport type TerminalPlannerOutcome = Extract<\n\tPlannerOutcome,\n\t{ kind: \"rateLimited\" | \"unusable\" | \"overflowed\" | \"providerError\" | \"policyRefusal\" }\n>;\n\n/** Whether this outcome produced validated deletion ranges. */\nexport function isPlannerSuccess(\n\toutcome: PlannerOutcome,\n): outcome is Extract<PlannerOutcome, { kind: \"ranked\" | \"recovered\" }> {\n\treturn outcome.kind === \"ranked\" || outcome.kind === \"recovered\";\n}\n\nfunction buildProviderErrorPattern(patterns: readonly string[]): RegExp {\n\treturn new RegExp(patterns.join(\"|\"), \"i\");\n}\n\n/**\n * Quota/billing exhaustion. Mirrors pi-ai's\n * `NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN` (`utils/retry.js`), which takes\n * precedence over the transient patterns and never spends backoff.\n */\nconst QUOTA_EXHAUSTED_PATTERN = buildProviderErrorPattern([\n\t\"GoUsageLimitError\",\n\t\"FreeUsageLimitError\",\n\t\"Monthly usage limit reached\",\n\t\"available balance\",\n\t\"insufficient_quota\",\n\t\"out of budget\",\n\t\"quota exceeded\",\n\t\"billing\",\n]);\n\n/**\n * Generic usage-limit wording, e.g. Codex's \"The usage limit has been reached\".\n *\n * This is a deliberate **local addition**, not a mirror: pi-ai lists it in\n * neither its non-retryable nor its retryable pattern, so `retryAssistantCall`\n * never schedules a backoff for it. Classifying it as transient throttling would\n * report `exhausted: true` for a retry budget that was never spent, so Atomic\n * treats it as quota exhaustion (`exhausted: false`). pi-ai's own\n * `Monthly usage limit reached` entry above already covers the specific form.\n */\nconst LOCAL_USAGE_LIMIT_PATTERN = /usage.?limit/i;\n\n/**\n * Transient throttling and provider-load failures. This is the rate-limit\n * subset of pi-ai's `RETRYABLE_PROVIDER_ERROR_PATTERN`, minus the individual\n * status codes, which `HTTP_5XX_PATTERN` generalizes below.\n */\nconst RATE_LIMITED_PATTERN = buildProviderErrorPattern([\n\t\"overloaded\",\n\t\"rate.?limit\",\n\t\"too many requests\",\n\t// Not part of a longer word or number, such as a request id or byte count; pi-ai matches it the same way.\n\t\"(?<![a-z0-9])429(?![a-z0-9])\",\n\t\"service.?unavailable\",\n\t\"server.?error\",\n\t\"internal.?error\",\n\t\"ResourceExhausted\",\n]);\n\n/**\n * The whole HTTP 5xx class, bounded so it cannot match inside a longer number.\n *\n * A deliberate local broadening: the RFC and the acceptance contract enumerate\n * `5xx`, while pi-ai lists only 500/502/503/504/524, so a `501` would otherwise\n * fall through to `providerError`. The consequence is that pi-ai may schedule no\n * backoff for a status Atomic types as rate limiting — which is exactly why\n * `exhausted` reports *observed* retry activity rather than assuming it.\n */\nconst HTTP_5XX_PATTERN = /(?<![\\d.])5\\d\\d(?![\\d.])/;\n\n/** How a failed planner response is classified before an outcome is built. */\nexport type PlannerFailureClass = \"overflow\" | \"quota\" | \"rate_limited\" | \"policy_refusal\" | \"provider_error\";\n\nexport function isProviderPolicyRefusal(message: string): boolean {\n\treturn (\n\t\t/^The model refused to complete the request[.!]?$/i.test(message) ||\n\t\t/terms of service|usage[\\s_-]*policy|acceptable use policy|content[\\s_-]*policy|policy violation|violat(?:e|es|ed|ion|ing)[\\s\\S]*policy|blocked[\\s\\S]*(?:policy|reverse engineering|duplicating model outputs)/i.test(\n\t\t\tmessage,\n\t\t)\n\t);\n}\n\nexport function isProviderPolicyRefusalResponse(response: AssistantMessage): boolean {\n\treturn response.rawStopReason === \"refusal\" || isProviderPolicyRefusal(response.errorMessage ?? \"\");\n}\n/**\n * Classify one failed planner response.\n *\n * Policy refusal wins before overflow, quota, and retryable-provider matching.\n * Context overflow is recoverable by trimming; quota wins over throttling.\n */\nexport function classifyPlannerFailure(response: AssistantMessage, contextWindow: number): PlannerFailureClass {\n\tif (isProviderPolicyRefusalResponse(response)) return \"policy_refusal\";\n\tif (isContextOverflow(response, contextWindow)) return \"overflow\";\n\tconst message = response.errorMessage ?? \"\";\n\tif (QUOTA_EXHAUSTED_PATTERN.test(message) || LOCAL_USAGE_LIMIT_PATTERN.test(message)) return \"quota\";\n\tif (RATE_LIMITED_PATTERN.test(message) || HTTP_5XX_PATTERN.test(message)) return \"rate_limited\";\n\treturn \"provider_error\";\n}\n\n/** Build a synthetic error response so a thrown transport failure classifies identically. */\nexport function syntheticErrorResponse(\n\tmodel: { api: Api; provider: string; id: string },\n\terrorMessage: string,\n): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: \"error\",\n\t\terrorMessage,\n\t\ttimestamp: Date.now(),\n\t} as AssistantMessage;\n}\n\n/**\n * Reasoning starvation fingerprint: the response stopped at the output limit,\n * produced no usable deletion record, and billed reasoning tokens.\n *\n * This is a diagnostic category only. Codex has no starvation concept and never\n * retries one; neither does Atomic. A starved outcome advances the ladder\n * exactly like any other `unusable` output.\n */\nexport function isReasoningStarved(response: AssistantMessage, hasUsableRanges: boolean): boolean {\n\treturn response.stopReason === \"length\" && !hasUsableRanges && (response.usage?.reasoning ?? 0) > 0;\n}\n"]}
|
|
@@ -9,6 +9,8 @@ export interface ModelRoutingContext {
|
|
|
9
9
|
getRouterModel(): string;
|
|
10
10
|
/** Provider and model filters from settings.json `modelRouting`; absent means every model is a candidate. */
|
|
11
11
|
getModelRouting?(): ModelRoutingSettings;
|
|
12
|
+
/** The `httpIdleTimeoutMs` setting; bounds each routing inference. Absent means the setting's default. */
|
|
13
|
+
getHttpIdleTimeoutMs?(): number;
|
|
12
14
|
}
|
|
13
15
|
export interface ModelRoute {
|
|
14
16
|
readonly routerSelection: ModelRouterOutput;
|
|
@@ -31,6 +33,21 @@ export declare class AutoRoutingInferenceError extends Error {
|
|
|
31
33
|
readonly currentModelRoute?: ModelRoute;
|
|
32
34
|
constructor(message: string, currentModelRoute?: ModelRoute);
|
|
33
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Caller-owned bounds on routing inference, on top of the structured-decision
|
|
38
|
+
* deadline that already limits each inference call. A caller that needs a
|
|
39
|
+
* shorter wait, or a concurrency limit, supplies them.
|
|
40
|
+
*/
|
|
41
|
+
export interface RoutingInferenceBounds {
|
|
42
|
+
/**
|
|
43
|
+
* When it aborts, inference stops and routing ends like any total inference
|
|
44
|
+
* failure, so a consumer may degrade to `AutoRoutingInferenceError.currentModelRoute`.
|
|
45
|
+
* Cancellation still goes through `signal`.
|
|
46
|
+
*/
|
|
47
|
+
readonly deadline?: AbortSignal;
|
|
48
|
+
/** Runs each inference under the caller's concurrency limit; the queue wait must end when `signal` aborts. */
|
|
49
|
+
readonly limit?: <T>(run: () => Promise<T>, signal: AbortSignal | undefined) => Promise<T>;
|
|
50
|
+
}
|
|
34
51
|
export declare function routeExecutionModel(input: {
|
|
35
52
|
ctx: ModelRoutingContext;
|
|
36
53
|
task: string;
|
|
@@ -51,5 +68,6 @@ export declare function routeExecutionModel(input: {
|
|
|
51
68
|
* candidates on top of the settings and never lift a user's exclusion.
|
|
52
69
|
*/
|
|
53
70
|
overrideProviderSettings?: boolean;
|
|
71
|
+
inference?: RoutingInferenceBounds;
|
|
54
72
|
}): Promise<ModelRoute>;
|
|
55
73
|
//# sourceMappingURL=execution-model-router.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution-model-router.d.ts","sourceRoot":"","sources":["../../src/core/execution-model-router.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,KAAK,GAAG,EAKR,KAAK,KAAK,EACV,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAUzD,OAAO,EAEN,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EAEtB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EAMN,KAAK,SAAS,EACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,aAAa,EAAE,IAAI,CAC3B,aAAa,EACb,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,8BAA8B,CAC3E,GACA,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,uBAAuB,GAAG,iBAAiB,GAAG,oBAAoB,GAAG,UAAU,CAAC,CAAC,CAAC;IAC/G,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,cAAc,IAAI,MAAM,CAAC;IACzB,6GAA6G;IAC7G,eAAe,CAAC,IAAI,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"execution-model-router.d.ts","sourceRoot":"","sources":["../../src/core/execution-model-router.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,KAAK,GAAG,EAKR,KAAK,KAAK,EACV,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAUzD,OAAO,EAEN,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EAEtB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EAMN,KAAK,SAAS,EACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,aAAa,EAAE,IAAI,CAC3B,aAAa,EACb,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,8BAA8B,CAC3E,GACA,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,uBAAuB,GAAG,iBAAiB,GAAG,oBAAoB,GAAG,UAAU,CAAC,CAAC,CAAC;IAC/G,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,cAAc,IAAI,MAAM,CAAC;IACzB,6GAA6G;IAC7G,eAAe,CAAC,IAAI,oBAAoB,CAAC;IACzC,0GAA0G;IAC1G,oBAAoB,CAAC,IAAI,MAAM,CAAC;CAChC;AACD,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,aAAa,IAAI,IAAI,CAAC;IACtB,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACzD;AAED;;;;GAIG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IACnD;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAExC,YAAY,OAAO,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,UAAU,EAI1D;CACD;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC;IAChC,8GAA8G;IAC9G,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,GAAG,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC3F;AAgDD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAChD,GAAG,EAAE,mBAAmB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,WAAW,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC1C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,kEAAkE;IAClE,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,+FAA+F;IAC/F,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACnC,GAAG,OAAO,CAAC,UAAU,CAAC,CAkZtB"}
|
|
@@ -30,6 +30,7 @@ export class AutoRoutingInferenceError extends Error {
|
|
|
30
30
|
this.currentModelRoute = currentModelRoute;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
const INFERENCE_DEADLINE_MESSAGE = "Auto routing timed out waiting for the routing model; the provider may be unavailable or overloaded.";
|
|
33
34
|
/** Degraded routes keep a balanced effort when the constraints leave a choice. */
|
|
34
35
|
const CURRENT_MODEL_EFFORT_PREFERENCE = [
|
|
35
36
|
null,
|
|
@@ -124,7 +125,10 @@ export async function routeExecutionModel(input) {
|
|
|
124
125
|
const candidates = [...new Set(available.map((entry) => `${entry.model.provider}/${entry.model.id}`))];
|
|
125
126
|
let selection = input.selection;
|
|
126
127
|
if (selection === undefined) {
|
|
127
|
-
const settings = {
|
|
128
|
+
const settings = {
|
|
129
|
+
getRouterModel: () => ctx.getRouterModel(),
|
|
130
|
+
...(ctx.getHttpIdleTimeoutMs ? { getHttpIdleTimeoutMs: () => ctx.getHttpIdleTimeoutMs() } : {}),
|
|
131
|
+
};
|
|
128
132
|
const router = resolveRouterModel({ settings, currentModel: ctx.model, modelRegistry: ctx.modelRegistry });
|
|
129
133
|
if (!input.task.trim())
|
|
130
134
|
throw new Error("Auto routing requires task instructions.");
|
|
@@ -156,13 +160,15 @@ export async function routeExecutionModel(input) {
|
|
|
156
160
|
};
|
|
157
161
|
// Any failure before a model is chosen is a total inference failure: the
|
|
158
162
|
// router and its chat structured-output fallback both failed (#3206).
|
|
163
|
+
const { deadline, limit } = input.inference ?? {};
|
|
159
164
|
const infer = async (decide) => {
|
|
165
|
+
const inferenceSignal = deadline === undefined ? signal : signal === undefined ? deadline : AbortSignal.any([signal, deadline]);
|
|
160
166
|
try {
|
|
161
|
-
return await decide();
|
|
167
|
+
return await (limit ? limit(() => decide(inferenceSignal), inferenceSignal) : decide(inferenceSignal));
|
|
162
168
|
}
|
|
163
169
|
catch (error) {
|
|
164
170
|
signal?.throwIfAborted();
|
|
165
|
-
throw new AutoRoutingInferenceError(error instanceof Error ? error.message : String(error), await currentModelRoute().catch(() => undefined));
|
|
171
|
+
throw new AutoRoutingInferenceError(deadline?.aborted ? INFERENCE_DEADLINE_MESSAGE : error instanceof Error ? error.message : String(error), await currentModelRoute().catch(() => undefined));
|
|
166
172
|
}
|
|
167
173
|
};
|
|
168
174
|
// Where code cannot pick a model either, the current chat model takes over
|
|
@@ -223,8 +229,8 @@ export async function routeExecutionModel(input) {
|
|
|
223
229
|
: {}),
|
|
224
230
|
}
|
|
225
231
|
: undefined;
|
|
226
|
-
const result = await infer(() => routeModel({
|
|
227
|
-
settings: { getRouterModel: () => reader },
|
|
232
|
+
const result = await infer((inferenceSignal) => routeModel({
|
|
233
|
+
settings: { ...settings, getRouterModel: () => reader },
|
|
228
234
|
modelRegistry: ctx.modelRegistry,
|
|
229
235
|
currentModel: ctx.model,
|
|
230
236
|
state: {
|
|
@@ -242,7 +248,7 @@ export async function routeExecutionModel(input) {
|
|
|
242
248
|
])),
|
|
243
249
|
decode: (choices) => Object.fromEntries(questions.map((question) => [question.id, choices[question.id]])),
|
|
244
250
|
},
|
|
245
|
-
signal,
|
|
251
|
+
signal: inferenceSignal,
|
|
246
252
|
}, (value) => questions.every((question) => Object.hasOwn(question.criteria, String(value[question.id])))));
|
|
247
253
|
answers = result.value;
|
|
248
254
|
}
|
|
@@ -310,7 +316,7 @@ export async function routeExecutionModel(input) {
|
|
|
310
316
|
if (shortlist.length > 1) {
|
|
311
317
|
const keys = shortlist.map((_, index) => `m${index}`);
|
|
312
318
|
const schema = Type.Object({ modelId: Type.String({ enum: shortlist.map((option) => option.model) }) }, { additionalProperties: false });
|
|
313
|
-
const result = await infer(() => routeModel({
|
|
319
|
+
const result = await infer((inferenceSignal) => routeModel({
|
|
314
320
|
settings,
|
|
315
321
|
modelRegistry: ctx.modelRegistry,
|
|
316
322
|
currentModel: ctx.model,
|
|
@@ -333,7 +339,7 @@ export async function routeExecutionModel(input) {
|
|
|
333
339
|
return { modelId: option.model };
|
|
334
340
|
},
|
|
335
341
|
},
|
|
336
|
-
signal,
|
|
342
|
+
signal: inferenceSignal,
|
|
337
343
|
}, (value) => shortlist.some((option) => option.model === value.modelId)));
|
|
338
344
|
chosen = shortlist.find((option) => option.model === result.value.modelId);
|
|
339
345
|
}
|