@oh-my-pi/pi-coding-agent 17.2.1 → 17.2.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 +47 -0
- package/dist/{CHANGELOG-dj46zzrm.md → CHANGELOG-xfpkakrn.md} +47 -0
- package/dist/cli.js +3891 -3885
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/settings-schema.d.ts +16 -5
- package/dist/types/edit/hashline/block-resolver.d.ts +5 -5
- package/dist/types/edit/hashline/diff.d.ts +4 -4
- package/dist/types/eval/executor-base.d.ts +27 -0
- package/dist/types/eval/js/context-manager.d.ts +7 -0
- package/dist/types/eval/js/executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/runner.d.ts +64 -5
- package/dist/types/extensibility/extensions/types.d.ts +15 -0
- package/dist/types/extensibility/extensions/wrapper.d.ts +1 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
- package/dist/types/live/visualizer.d.ts +3 -1
- package/dist/types/modes/acp/acp-agent.d.ts +3 -3
- package/dist/types/modes/components/keybinding-hints.d.ts +1 -1
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +13 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/secrets/index.d.ts +22 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session-types.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/session/codex-auto-reset.d.ts +182 -64
- package/dist/types/session/session-storage.d.ts +5 -2
- package/dist/types/session/session-tools.d.ts +2 -1
- package/dist/types/session/turn-recovery.d.ts +7 -1
- package/dist/types/system-prompt.d.ts +8 -0
- package/dist/types/task/executor.d.ts +27 -0
- package/dist/types/task/persisted-revive.d.ts +7 -0
- package/dist/types/tools/bash-interceptor.d.ts +1 -1
- package/dist/types/tools/browser/attach.d.ts +9 -1
- package/dist/types/tools/browser/launch.d.ts +20 -1
- package/dist/types/tools/browser/registry.d.ts +2 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +8 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +1 -0
- package/dist/types/tools/browser/tab-worker.d.ts +3 -1
- package/dist/types/tools/shell-tokenize.d.ts +11 -0
- package/dist/types/utils/image-loading.d.ts +2 -0
- package/dist/types/web/search/providers/duckduckgo.d.ts +9 -0
- package/dist/types/web/search/types.d.ts +2 -2
- package/package.json +12 -12
- package/src/commit/agentic/index.ts +2 -1
- package/src/commit/pipeline.ts +3 -2
- package/src/config/keybindings.ts +6 -1
- package/src/config/settings-schema.ts +16 -3
- package/src/edit/hashline/block-resolver.ts +5 -5
- package/src/edit/hashline/diff.ts +22 -15
- package/src/edit/hashline/execute.ts +14 -13
- package/src/edit/renderer.ts +3 -3
- package/src/eval/executor-base.ts +39 -0
- package/src/eval/jl/executor.ts +13 -2
- package/src/eval/js/context-manager.ts +97 -14
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/index.ts +1 -0
- package/src/eval/py/executor.ts +55 -15
- package/src/eval/rb/executor.ts +8 -1
- package/src/extensibility/extensions/runner.ts +121 -4
- package/src/extensibility/extensions/types.ts +15 -0
- package/src/extensibility/extensions/wrapper.ts +19 -2
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +5 -5
- package/src/live/visualizer.ts +8 -1
- package/src/lsp/index.ts +51 -11
- package/src/main.ts +3 -0
- package/src/modes/acp/acp-agent.ts +16 -5
- package/src/modes/components/assistant-message.ts +5 -10
- package/src/modes/components/custom-editor.ts +1 -1
- package/src/modes/components/keybinding-hints.ts +4 -13
- package/src/modes/components/status-line/segments.ts +4 -5
- package/src/modes/components/tool-execution.ts +4 -5
- package/src/modes/controllers/input-controller.ts +18 -40
- package/src/modes/controllers/live-command-controller.ts +1 -0
- package/src/modes/controllers/mcp-command-controller.ts +28 -11
- package/src/modes/interactive-mode.ts +59 -2
- package/src/modes/theme/theme.ts +13 -6
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/prompts/system/plan-mode-active.md +5 -5
- package/src/sdk.ts +47 -16
- package/src/secrets/index.ts +83 -3
- package/src/secrets/obfuscator.ts +70 -15
- package/src/session/agent-session-types.ts +7 -1
- package/src/session/agent-session.ts +393 -112
- package/src/session/codex-auto-reset.ts +594 -123
- package/src/session/eval-runner.ts +2 -0
- package/src/session/messages.ts +1 -1
- package/src/session/session-maintenance.ts +48 -1
- package/src/session/session-manager.ts +4 -3
- package/src/session/session-stats.ts +14 -3
- package/src/session/session-storage.ts +47 -15
- package/src/session/session-tools.ts +30 -2
- package/src/session/turn-recovery.ts +15 -2
- package/src/slash-commands/builtin-registry.ts +21 -15
- package/src/slash-commands/helpers/reset-usage.ts +2 -0
- package/src/system-prompt.ts +13 -1
- package/src/task/executor.ts +168 -15
- package/src/task/persisted-revive.ts +26 -1
- package/src/telemetry-export.ts +8 -4
- package/src/tools/bash-interceptor.ts +85 -8
- package/src/tools/bash.ts +1 -1
- package/src/tools/browser/attach.ts +35 -10
- package/src/tools/browser/launch.ts +60 -11
- package/src/tools/browser/registry.ts +18 -2
- package/src/tools/browser/tab-protocol.ts +8 -0
- package/src/tools/browser/tab-supervisor.ts +31 -4
- package/src/tools/browser/tab-worker.ts +32 -2
- package/src/tools/output-meta.ts +16 -1
- package/src/tools/shell-tokenize.ts +101 -0
- package/src/utils/image-loading.ts +8 -3
- package/src/utils/shell-snapshot.ts +31 -21
- package/src/web/search/providers/duckduckgo.ts +183 -15
- package/src/web/search/types.ts +6 -1
|
@@ -1,111 +1,229 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Pool-wide planner for spending saved OpenAI Codex rate-limit resets, plus
|
|
3
|
+
* the process-wide coordinator that serializes attempts.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
5
|
+
* A saved reset is a scarce, ~monthly credit — but it is also perishable:
|
|
6
|
+
* every credit carries an `expiresAt`, and an expired credit is worth exactly
|
|
7
|
+
* nothing. The planner therefore balances two failure modes instead of only
|
|
8
|
+
* one: wasting a credit on a window that would have healed anyway, and letting
|
|
9
|
+
* a credit die unspent. Two rules, evaluated over EVERY stored Codex account
|
|
10
|
+
* (not just the session's active one):
|
|
11
|
+
*
|
|
12
|
+
* - `expiring-credit` (salvage; any trigger): the account's soonest available
|
|
13
|
+
* credit expires within `salvageHorizonMs` and the weekly window is at least
|
|
14
|
+
* {@link SALVAGE_MIN_USED_FRACTION} used, so redeeming restores real quota.
|
|
15
|
+
* The `keepCredits` reserve is deliberately ignored here — reserving a
|
|
16
|
+
* credit that is about to expire preserves nothing. If the window resets
|
|
17
|
+
* naturally before the credit expires, the next sweep sees a mostly-free
|
|
18
|
+
* window and skips: the credit had nothing left to restore. The backend may
|
|
19
|
+
* refuse a partial-usage consume with `nothing_to_reset`; that outcome is
|
|
20
|
+
* NON-terminal — the episode is deferred (not buried), so the credit is
|
|
21
|
+
* retried as usage grows or a window exhausts before expiry.
|
|
22
|
+
* - `blocked-account` (restore; `blocked` trigger only): a live 429 blocked
|
|
23
|
+
* the turn and no sibling credential could take over, so the pool is dry.
|
|
24
|
+
* Candidates are accounts with at least one genuinely exhausted chat window
|
|
25
|
+
* — 5h primary or weekly secondary. A banked reset clears the account's
|
|
26
|
+
* chat rate limits generally, not just the weekly window: OpenAI's own
|
|
27
|
+
* client consumes one for a 5h-only block while the weekly window still has
|
|
28
|
+
* ~80% headroom (openai/codex#28525). The natural unblock is the LATEST
|
|
29
|
+
* reset among the exhausted windows (the account stays blocked until every
|
|
30
|
+
* one rolls over) and must be far enough away to justify the spend, with
|
|
31
|
+
* credits above the reserve. One candidate is redeemed — active account
|
|
32
|
+
* first, then the account whose credit dies soonest — and the redeem clears
|
|
33
|
+
* its credential blocks so the retry's re-rank picks it up.
|
|
34
|
+
*
|
|
35
|
+
* TRIGGERS: `blocked` runs from the usage-limit branch of the retry pipeline
|
|
36
|
+
* after sibling switch fails, on force-refreshed reports (the cached snapshot
|
|
37
|
+
* predates the 429 that got us there). `sweep` piggybacks on every successful
|
|
38
|
+
* usage-report fetch — the status line polls every 5 minutes while the TUI is
|
|
39
|
+
* open — so expiring credits are caught even when nothing is blocked.
|
|
17
40
|
*
|
|
18
41
|
* THE DECISION-2 TRAP (status MUST NOT be used to find the blocker):
|
|
19
42
|
* `openai-codex.ts` applies the top-level `rate_limit.limit_reached` flag to
|
|
20
43
|
* BOTH the primary (5h) and secondary (weekly) `buildUsageLimit` calls, so when
|
|
21
44
|
* an account is blocked, *both* limit entries carry `status: "exhausted"`
|
|
22
45
|
* regardless of which window is actually at 100%. Only `amount.usedFraction`
|
|
23
|
-
* disambiguates
|
|
24
|
-
*
|
|
25
|
-
*
|
|
46
|
+
* disambiguates. This module keys eligibility off exact limit ids
|
|
47
|
+
* (`openai-codex:primary` / `openai-codex:secondary`) and `usedFraction`,
|
|
48
|
+
* never off `status`, so the plan names the true blocking window(s) and their
|
|
49
|
+
* real unblock time. Whether a short wait (e.g. a 5h-only block) is worth a
|
|
50
|
+
* credit is the `minBlockedMinutes` knob's call, not hardcoded fiat.
|
|
26
51
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* usage report for the active account must confirm `limitReached`; the WEEKLY
|
|
31
|
-
* (secondary) window must be genuinely exhausted — a 5h-only block self-heals
|
|
32
|
-
* within the hour, so a credit spent there buys nothing; the natural reset must be far
|
|
33
|
-
* enough away to justify spending a ~30-day credit yet within one plausible
|
|
34
|
-
* window length; a credit must be verifiably available above the reserve; and
|
|
35
|
-
* the same block episode must not have been attempted already (debounce +
|
|
36
|
-
* per-account cooldown). All of this is pure — no fetches, no IO. The only
|
|
37
|
-
* stateful piece is the {@link CodexAutoRedeemCoordinator} container, whose
|
|
38
|
-
* read-only views are passed in so the predicate itself stays deterministic.
|
|
52
|
+
* All of this is pure — no fetches, no IO. The only stateful piece is the
|
|
53
|
+
* {@link CodexAutoRedeemCoordinator} container, whose read-only views are
|
|
54
|
+
* passed in so the planner itself stays deterministic.
|
|
39
55
|
*/
|
|
40
|
-
import type { OAuthAccountIdentity, ResetCreditTarget, UsageReport } from "@oh-my-pi/pi-ai";
|
|
56
|
+
import type { OAuthAccountIdentity, ResetCreditAccountStatus, ResetCreditTarget, UsageReport } from "@oh-my-pi/pi-ai";
|
|
41
57
|
import type { CodexAutoRedeemMode } from "../config/settings-schema.js";
|
|
42
|
-
/**
|
|
43
|
-
export declare const
|
|
58
|
+
/** A chat window counts as exhausted at `usedFraction >= 0.999` (used_percent >= 99.9). */
|
|
59
|
+
export declare const WINDOW_EXHAUSTED_MIN_FRACTION = 0.999;
|
|
44
60
|
/** A weekly reset can never be more than one window length (7d) away; +1h slack for skew. */
|
|
45
|
-
export declare const
|
|
61
|
+
export declare const MAX_PLAUSIBLE_WEEKLY_REMAINING_MS: number;
|
|
62
|
+
/** A 5h reset can never be more than one window length (5h) away; +1h slack for skew. */
|
|
63
|
+
export declare const MAX_PLAUSIBLE_PRIMARY_REMAINING_MS: number;
|
|
64
|
+
/** Below this usage on BOTH chat windows a salvaged reset restores too little to bother (and risks a `nothing_to_reset` no-op). */
|
|
65
|
+
export declare const SALVAGE_MIN_USED_FRACTION = 0.25;
|
|
66
|
+
/** Retry spacing after a non-terminal consume outcome (`nothing_to_reset`, transport failure). */
|
|
67
|
+
export declare const REDEEM_RETRY_DEFER_MS: number;
|
|
46
68
|
/** Report must be no older than the 5-min usage cache TTL plus slack. */
|
|
47
69
|
export declare const REPORT_FRESHNESS_MS: number;
|
|
48
|
-
/** Per-account cooldown that catches
|
|
70
|
+
/** Per-account cooldown that catches attempt-key drift across a minute boundary. */
|
|
49
71
|
export declare const ATTEMPT_COOLDOWN_MS = 60000;
|
|
50
|
-
/** Minute bucket for
|
|
72
|
+
/** Minute bucket for attempt keys, absorbing `reset_after_seconds`/expiry jitter. */
|
|
51
73
|
export declare const DEBOUNCE_BUCKET_MS = 60000;
|
|
74
|
+
/** Floor between salvage sweeps; dedupe keys make sweeps idempotent, this just avoids useless re-planning. */
|
|
75
|
+
export declare const SWEEP_MIN_INTERVAL_MS = 60000;
|
|
52
76
|
export declare function shouldEvaluateCodexAutoRedeem(mode: CodexAutoRedeemMode): boolean;
|
|
53
77
|
export declare function shouldPromptCodexAutoRedeem(mode: CodexAutoRedeemMode): boolean;
|
|
54
|
-
|
|
55
|
-
export
|
|
78
|
+
/** What woke the planner. `sweep` may only salvage; `blocked` may also restore. */
|
|
79
|
+
export type CodexResetTrigger = "blocked" | "sweep";
|
|
80
|
+
/** Why one account produced no action (or, with `accountKey: "*"`, a whole rule was off). */
|
|
81
|
+
export type CodexResetSkipReason = "disabled" | "wrong-provider" | "spark-model" | "no-identity" | "stale-report" | "not-limit-reached" | "no-exhausted-window" | "deferred" | "no-reset-time" | "reset-too-soon" | "reset-implausible" | "credits-unknown" | "no-credits" | "reserve" | "no-expiring-credit" | "window-mostly-free" | "already-attempted" | "cooldown";
|
|
82
|
+
export interface CodexResetPlanInput {
|
|
56
83
|
nowMs: number;
|
|
57
|
-
|
|
84
|
+
trigger: CodexResetTrigger;
|
|
85
|
+
/** `this.model.provider` — gates the `blocked-account` rule only. */
|
|
58
86
|
provider: string;
|
|
59
|
-
/** `this.model.id
|
|
87
|
+
/** `this.model.id` — gates the `blocked-account` rule only. */
|
|
60
88
|
modelId: string;
|
|
61
89
|
settings: {
|
|
62
|
-
|
|
90
|
+
enabled: boolean;
|
|
91
|
+
/** `blocked-account`: skip when the natural unblock is closer than this. */
|
|
63
92
|
minBlockedMinutes: number;
|
|
93
|
+
/** `blocked-account`: never spend below this many remaining credits. */
|
|
64
94
|
keepCredits: number;
|
|
95
|
+
/** `expiring-credit`: salvage window; `<= 0` disables the rule. */
|
|
96
|
+
salvageHorizonMs: number;
|
|
65
97
|
};
|
|
66
|
-
/**
|
|
98
|
+
/** Active account (marks the preferred restore candidate); may be undefined. */
|
|
67
99
|
identity: OAuthAccountIdentity | undefined;
|
|
68
|
-
/**
|
|
100
|
+
/** Usage reports for ALL stored accounts (one per account for Codex). */
|
|
69
101
|
reports: UsageReport[] | null;
|
|
70
|
-
|
|
102
|
+
attemptedKeys: ReadonlySet<string>;
|
|
103
|
+
/** Episodes parked by a non-terminal consume outcome, keyed by attempt key (epoch ms). */
|
|
104
|
+
deferredUntilByKey: ReadonlyMap<string, number>;
|
|
71
105
|
lastAttemptAtByAccount: ReadonlyMap<string, number>;
|
|
106
|
+
/**
|
|
107
|
+
* Live 429 evidence for the ACTIVE account: absolute epoch ms when the
|
|
108
|
+
* provider said the account unblocks, derived from the usage-limit error's
|
|
109
|
+
* parsed retry hint AT THE ERROR (absolute, so slow usage IO between the
|
|
110
|
+
* error and planning cannot drift it). Authoritative when the usage report
|
|
111
|
+
* is stale or missing — the report layer can adopt a pre-block in-flight
|
|
112
|
+
* fetch or serve the last-good snapshot when `/wham/usage` fails (it is
|
|
113
|
+
* IP-throttled, so failure right after a 429 is common), and such a
|
|
114
|
+
* snapshot still shows `limitReached: false` with healthy windows. With no
|
|
115
|
+
* usable report at all, a candidate is synthesized from `identity` and the
|
|
116
|
+
* redeem re-checks credits live. Only used on `blocked`.
|
|
117
|
+
*/
|
|
118
|
+
activeBlockUnblockAtMs?: number;
|
|
72
119
|
}
|
|
73
|
-
|
|
74
|
-
|
|
120
|
+
/** One credit to spend. `redeemResetCredit` picks the account's soonest-expiring credit. */
|
|
121
|
+
export interface CodexResetAction {
|
|
122
|
+
reason: "blocked-account" | "expiring-credit";
|
|
75
123
|
target: ResetCreditTarget;
|
|
76
124
|
accountKey: string;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
125
|
+
/** Once-per-episode dedupe key; record in `attemptedKeys` BEFORE consuming. */
|
|
126
|
+
attemptKey: string;
|
|
127
|
+
/** Human label for notices/prompts (email preferred). */
|
|
128
|
+
label: string;
|
|
129
|
+
/** Redeemable credits per the report; undefined for a synthesized live-429 candidate. */
|
|
130
|
+
availableCount?: number;
|
|
131
|
+
weeklyUsedFraction?: number;
|
|
132
|
+
/** `blocked-account`: ms until the natural unblock (latest exhausted-window reset). */
|
|
133
|
+
remainingMs?: number;
|
|
134
|
+
/** `blocked-account`: the exhausted chat windows a redeem would clear. */
|
|
135
|
+
blockedWindows?: ("5h" | "weekly")[];
|
|
136
|
+
/** `expiring-credit`: the fuller chat window a redeem restores (for messaging). */
|
|
137
|
+
salvageWindow?: "5h" | "weekly";
|
|
138
|
+
/** `expiring-credit`: used fraction of {@link CodexResetAction.salvageWindow}. */
|
|
139
|
+
salvageUsedFraction?: number;
|
|
140
|
+
/** `expiring-credit`: ms until the credit expires. */
|
|
141
|
+
expiresInMs?: number;
|
|
142
|
+
/** True when this is the session's active account. */
|
|
143
|
+
active: boolean;
|
|
144
|
+
}
|
|
145
|
+
export interface CodexResetSkip {
|
|
146
|
+
/** Normalized account key, or `"*"` for a rule-wide gate. */
|
|
147
|
+
accountKey: string;
|
|
148
|
+
rule: "blocked-account" | "expiring-credit" | "account";
|
|
149
|
+
reason: CodexResetSkipReason;
|
|
150
|
+
}
|
|
151
|
+
export interface CodexResetPlan {
|
|
152
|
+
/** At most one `blocked-account` action (first), then salvages by soonest expiry. */
|
|
153
|
+
actions: CodexResetAction[];
|
|
154
|
+
/** Diagnostics for `logger.debug` and tests. */
|
|
155
|
+
skipped: CodexResetSkip[];
|
|
81
156
|
}
|
|
82
|
-
export type CodexAutoRedeemDecision = CodexAutoRedeemRedeemDecision | {
|
|
83
|
-
redeem: false;
|
|
84
|
-
reason: CodexAutoRedeemSkipReason;
|
|
85
|
-
};
|
|
86
157
|
/**
|
|
87
|
-
*
|
|
158
|
+
* Plan which saved Codex resets to spend right now. Pure: a function of the
|
|
159
|
+
* snapshot inputs only. Callers execute the returned actions in order,
|
|
160
|
+
* re-checking `attemptedKeys` immediately before each consume.
|
|
161
|
+
*/
|
|
162
|
+
export declare function planCodexResetRedemptions(input: CodexResetPlanInput): CodexResetPlan;
|
|
163
|
+
/** One attempt per (account, weekly-reset-minute) block episode. */
|
|
164
|
+
export declare function blockedAttemptKey(accountKey: string, weeklyResetsAtMs: number): string;
|
|
165
|
+
/** One attempt per (account, credit-expiry-minute) salvage episode. */
|
|
166
|
+
export declare function salvageAttemptKey(accountKey: string, creditExpiresAtMs: number): string;
|
|
167
|
+
/**
|
|
168
|
+
* Overlay LIVE per-account credit state (from the dedicated
|
|
169
|
+
* `rate-limit-reset-credits` route) onto usage reports before a blocked pass.
|
|
88
170
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
171
|
+
* `/wham/usage` credit counts can be stale or pre-feature, and the usage
|
|
172
|
+
* provider only consults the live detail endpoint when the usage payload
|
|
173
|
+
* already reports a POSITIVE count — a stale ZERO is never corrected there.
|
|
174
|
+
* Live data therefore replaces the report's credit block wholesale; accounts
|
|
175
|
+
* with no live row (or a failed lookup) get the block stripped, so the planner
|
|
176
|
+
* treats them as `credits-unknown` instead of trusting a stale count: siblings
|
|
177
|
+
* stay conservative while the active account can still be synthesized from
|
|
178
|
+
* live 429 evidence (the redeem re-lists atomically either way).
|
|
179
|
+
*/
|
|
180
|
+
export declare function overlayLiveResetCredits(reports: UsageReport[] | null, statuses: readonly ResetCreditAccountStatus[]): UsageReport[] | null;
|
|
181
|
+
/**
|
|
182
|
+
* Whether a consume outcome permanently settles its episode. Terminal codes
|
|
183
|
+
* either spent the credit (`reset`), or prove this credit can never be spent
|
|
184
|
+
* (`already_redeemed`, `no_credit` — a live listing that really had nothing).
|
|
185
|
+
* Everything else — `nothing_to_reset` (limits not constrained enough right
|
|
186
|
+
* now), `credit_list_failed` (flaky listing), thrown transport failures,
|
|
187
|
+
* unknown codes — leaves the credit banked: the executor releases the attempt
|
|
188
|
+
* key and
|
|
189
|
+
* defers the episode by {@link REDEEM_RETRY_DEFER_MS} instead of burying a
|
|
190
|
+
* live credit for the rest of the process.
|
|
92
191
|
*/
|
|
93
|
-
export declare function
|
|
192
|
+
export declare function isTerminalRedeemOutcome(code: string): boolean;
|
|
94
193
|
/**
|
|
95
194
|
* Process-wide (NOT per-session) coordinator state. Parallel subagent sessions
|
|
96
195
|
* share the same Codex accounts and must not race a double-spend, so this is a
|
|
97
196
|
* single shared container, not a per-session field.
|
|
98
197
|
*
|
|
99
|
-
* - `
|
|
100
|
-
*
|
|
198
|
+
* - `attemptedKeys`: one attempt per episode key — recorded before calling the
|
|
199
|
+
* consume so exceptions can't re-enter. Non-terminal outcomes (see
|
|
200
|
+
* {@link isTerminalRedeemOutcome}) release the key again and park the
|
|
201
|
+
* episode in `deferredUntilByKey` instead.
|
|
202
|
+
* - `deferredUntilByKey`: earliest retry time for episodes whose consume was
|
|
203
|
+
* refused non-terminally (`nothing_to_reset`, transport failure).
|
|
101
204
|
* - `lastAttemptAtByAccount`: per-account cooldown timestamps (epoch ms),
|
|
102
|
-
* catching
|
|
103
|
-
* - `inFlightByAccount`: serializes per account — a second
|
|
104
|
-
* account adopts the in-flight promise instead of
|
|
205
|
+
* catching attempt-key drift across a minute boundary.
|
|
206
|
+
* - `inFlightByAccount`: serializes blocked passes per account — a second
|
|
207
|
+
* session for the same account adopts the in-flight promise instead of
|
|
208
|
+
* starting a second consume.
|
|
209
|
+
* - `sweepInFlight` / `lastSweepAt` / `sweepPromise`: re-entrancy guard, floor,
|
|
210
|
+
* and settlement handle for the salvage sweep (a redeem refreshes usage,
|
|
211
|
+
* which would recurse into a sweep; the promise lets tests and diagnostics
|
|
212
|
+
* await a fire-and-forget sweep instead of polling).
|
|
213
|
+
* - `notifiedKeys`: headless "run /usage reset" notices already emitted, so a
|
|
214
|
+
* 5-minute sweep cadence can't spam the transcript.
|
|
105
215
|
*/
|
|
106
216
|
export interface CodexAutoRedeemCoordinator {
|
|
107
|
-
|
|
217
|
+
attemptedKeys: Set<string>;
|
|
218
|
+
deferredUntilByKey: Map<string, number>;
|
|
108
219
|
lastAttemptAtByAccount: Map<string, number>;
|
|
109
220
|
inFlightByAccount: Map<string, Promise<boolean>>;
|
|
221
|
+
sweepInFlight: boolean;
|
|
222
|
+
lastSweepAt: number;
|
|
223
|
+
/** Settlement of the most recently scheduled sweep (never rejects). */
|
|
224
|
+
sweepPromise: Promise<void> | undefined;
|
|
225
|
+
notifiedKeys: Set<string>;
|
|
110
226
|
}
|
|
227
|
+
/** Fresh, empty coordinator: backs the process-wide default; inject one per test for isolation. */
|
|
228
|
+
export declare function createCodexAutoRedeemCoordinator(): CodexAutoRedeemCoordinator;
|
|
111
229
|
export declare const defaultCodexAutoRedeemCoordinator: CodexAutoRedeemCoordinator;
|
|
@@ -6,14 +6,17 @@ export interface SessionStorageStat {
|
|
|
6
6
|
}
|
|
7
7
|
export interface SessionStorageWriter {
|
|
8
8
|
/**
|
|
9
|
-
* Append one newline-terminated line. File
|
|
10
|
-
*
|
|
9
|
+
* Append one newline-terminated line. File storage batches same-turn appends
|
|
10
|
+
* until its microtask boundary; memory storage updates in-body; indexed
|
|
11
|
+
* backends queue in call order.
|
|
11
12
|
*
|
|
12
13
|
* `line` MUST include the trailing newline.
|
|
13
14
|
*/
|
|
14
15
|
append(line: string): Promise<void>;
|
|
15
16
|
/** Resolve once all queued appends complete. No fsync. */
|
|
16
17
|
flush(): Promise<void>;
|
|
18
|
+
/** Drain synchronously flushable queued work when the backend supports it. No fsync. */
|
|
19
|
+
flushSync?(): void;
|
|
17
20
|
/** False once close() has begun/finished. */
|
|
18
21
|
isOpen(): boolean;
|
|
19
22
|
close(): Promise<void>;
|
|
@@ -50,6 +50,7 @@ interface SessionToolsOptions {
|
|
|
50
50
|
ensureWriteRegistered?: () => Promise<boolean>;
|
|
51
51
|
rebuildSystemPrompt?: (toolNames: string[], tools: Map<string, AgentTool>) => Promise<{
|
|
52
52
|
systemPrompt: string[];
|
|
53
|
+
xdevCatalogNames?: readonly string[];
|
|
53
54
|
}>;
|
|
54
55
|
getLocalCalendarDate?: () => string;
|
|
55
56
|
getMcpServerInstructions?: () => Map<string, string> | undefined;
|
|
@@ -159,7 +160,7 @@ export declare class SessionTools {
|
|
|
159
160
|
*/
|
|
160
161
|
resetAnnouncedMounts(): void;
|
|
161
162
|
/** Consumes the hidden notice for unannounced `xd://` mount changes. */
|
|
162
|
-
takePendingXdevMountNotice(): CustomMessage<XdevMountNoticeDetails> | undefined;
|
|
163
|
+
takePendingXdevMountNotice(baseCatalogDelivered: boolean): CustomMessage<XdevMountNoticeDetails> | undefined;
|
|
163
164
|
/** Rediscovers reloadable skills and refreshes prompt metadata. */
|
|
164
165
|
refreshSkills(): Promise<void>;
|
|
165
166
|
/** Selects enabled tools, ignoring names absent from the registry. */
|
|
@@ -46,7 +46,13 @@ export interface TurnRecoveryHost {
|
|
|
46
46
|
sessionMessageAlreadyPersisted(message: AssistantMessage): boolean;
|
|
47
47
|
setModelWithProviderSessionReset(model: Model): Promise<void>;
|
|
48
48
|
resetCurrentResponsesProviderSession(reason: string): void;
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Spend a saved Codex reset for the blocked pool, if eligible.
|
|
51
|
+
* `activeBlockUnblockAtMs` is the absolute unblock time parsed from the
|
|
52
|
+
* live usage-limit error — authoritative for the active account when the
|
|
53
|
+
* usage report still shows a pre-block snapshot.
|
|
54
|
+
*/
|
|
55
|
+
maybeAutoRedeemCodexReset(activeBlockUnblockAtMs?: number): Promise<boolean>;
|
|
50
56
|
runAutoCompaction(reason: "overflow" | "threshold" | "idle" | "incomplete", willRetry: boolean, deferred?: boolean, allowDefer?: boolean, options?: {
|
|
51
57
|
autoContinue?: boolean;
|
|
52
58
|
triggerContextTokens?: number;
|
|
@@ -148,6 +148,14 @@ export interface BuildSystemPromptOptions {
|
|
|
148
148
|
export interface BuildSystemPromptResult {
|
|
149
149
|
/** Ordered system prompt blocks. Providers should preserve entries as distinct messages/blocks. */
|
|
150
150
|
systemPrompt: string[];
|
|
151
|
+
/**
|
|
152
|
+
* Names of `xd://` devices whose catalog/protocol section this prompt renders.
|
|
153
|
+
* Empty/undefined when no catalog was emitted (no mounted devices, or a custom
|
|
154
|
+
* prompt template that omits the section). Lets the session fold these devices
|
|
155
|
+
* into its announced-mount baseline so a same-turn mount notice does not re-list
|
|
156
|
+
* a catalog the prompt already carries (issue #7139).
|
|
157
|
+
*/
|
|
158
|
+
xdevCatalogNames?: readonly string[];
|
|
151
159
|
}
|
|
152
160
|
/** Build the system prompt with tools, guidelines, and context */
|
|
153
161
|
export declare function buildSystemPrompt(options?: BuildSystemPromptOptions): Promise<BuildSystemPromptResult>;
|
|
@@ -250,6 +250,33 @@ export declare function finalizeSubprocessOutput(args: FinalizeSubprocessOutputA
|
|
|
250
250
|
export declare function createMCPProxyTools(mcpManager: MCPManager): CustomTool[];
|
|
251
251
|
export declare function createSubagentSettings(baseSettings: Settings, overrides?: Partial<Record<SettingPath, unknown>>, inheritedServiceTier?: ServiceTierByFamily | null): Settings;
|
|
252
252
|
export type AbortReason = "signal" | "terminate" | "timeout" | "budget";
|
|
253
|
+
/** Inputs for {@link attachIrcWakeTurnMonitor}. */
|
|
254
|
+
export interface IrcWakeTurnMonitorOptions {
|
|
255
|
+
/** Registry id of the kept-alive subagent whose autonomous IRC wake turns are monitored. */
|
|
256
|
+
id: string;
|
|
257
|
+
index?: number;
|
|
258
|
+
agent: AgentDefinition;
|
|
259
|
+
description?: string;
|
|
260
|
+
modelOverride?: string | string[];
|
|
261
|
+
eventBus?: EventBus;
|
|
262
|
+
parentToolCallId?: string;
|
|
263
|
+
/** Fallback session file when the registry ref carries none. */
|
|
264
|
+
sessionFile?: string;
|
|
265
|
+
maxRuntimeMs?: number;
|
|
266
|
+
outputSchema?: unknown;
|
|
267
|
+
outputSchemaMode?: StructuredSubagentSchemaMode;
|
|
268
|
+
outputSchemaSource?: StructuredSubagentSchemaSource;
|
|
269
|
+
artifactsDir?: string;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Bracket a kept-alive subagent's autonomous IRC wake turns with a task run
|
|
273
|
+
* monitor so RPC/collab subscribers see the same `subagent_lifecycle` /
|
|
274
|
+
* `subagent_progress` frames a first run emits. Shared by the live executor
|
|
275
|
+
* reviver and the persisted cold-revive path so a resumed process's parked
|
|
276
|
+
* subagents are not blind spots. The observer runs after the session has
|
|
277
|
+
* flushed its post-prompt settle (see {@link AgentSession.setIrcWakeTurnObserver}).
|
|
278
|
+
*/
|
|
279
|
+
export declare function attachIrcWakeTurnMonitor(session: AgentSession, options: IrcWakeTurnMonitorOptions): void;
|
|
253
280
|
/**
|
|
254
281
|
* Settle a subagent's registry lifecycle after a run: terminal teardown for
|
|
255
282
|
* hard aborts, unregister for one-shot helpers, park for isolated runs, and
|
|
@@ -3,6 +3,7 @@ import type { Settings } from "../config/settings.js";
|
|
|
3
3
|
import type { PersistedSubagentReviverFactory } from "../registry/agent-lifecycle.js";
|
|
4
4
|
import type { AgentSession } from "../session/agent-session.js";
|
|
5
5
|
import type { AuthStorage } from "../session/auth-storage.js";
|
|
6
|
+
import type { EventBus } from "../utils/event-bus.js";
|
|
6
7
|
/**
|
|
7
8
|
* Ambient context the reviver needs at revive time. The top-level session is
|
|
8
9
|
* kept LIVE (cwd / artifact manager read on demand) so a later `/new` or cwd
|
|
@@ -16,6 +17,12 @@ export interface PersistedSubagentReviveContext {
|
|
|
16
17
|
settings: Settings;
|
|
17
18
|
/** LSP policy of the top-level session; revived subagents inherit it rather than defaulting on. */
|
|
18
19
|
enableLsp: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Shared event bus feeding RPC/collab subagent subscriptions. Passed through
|
|
22
|
+
* to the wake-turn monitor so an IRC send to a cold-revived subagent emits
|
|
23
|
+
* the same lifecycle/progress frames a live run does.
|
|
24
|
+
*/
|
|
25
|
+
eventBus?: EventBus;
|
|
19
26
|
}
|
|
20
27
|
/**
|
|
21
28
|
* Build the factory the {@link AgentLifecycleManager} uses to cold-revive a
|
|
@@ -21,4 +21,4 @@ export interface InterceptionResult {
|
|
|
21
21
|
* @param availableTools Set of tool names that are available
|
|
22
22
|
* @returns InterceptionResult indicating if the command should be blocked
|
|
23
23
|
*/
|
|
24
|
-
export declare function checkBashInterception(command: string, availableTools: string[], rules?: BashInterceptorRule[]): InterceptionResult;
|
|
24
|
+
export declare function checkBashInterception(command: string, availableTools: string[], rules?: BashInterceptorRule[], originalCommand?: string): InterceptionResult;
|
|
@@ -16,12 +16,20 @@ export declare function findReusableCdp(exe: string, signal?: AbortSignal): Prom
|
|
|
16
16
|
cdpUrl: string;
|
|
17
17
|
pid: number;
|
|
18
18
|
} | null>;
|
|
19
|
+
export declare function shouldPreserveConnectedBrowserFocus(target?: string): boolean;
|
|
19
20
|
/**
|
|
20
21
|
* Pick the best page target on an attached browser. Prefer discoverable page
|
|
21
22
|
* targets first so Chromium/Edge attach flows that hide pages from
|
|
22
23
|
* `browser.pages()` can still return a usable tab.
|
|
24
|
+
*
|
|
25
|
+
* `preferVisible` is for attaching to a browser a human is using: among equally
|
|
26
|
+
* usable tabs, take the one that is actually foregrounded rather than whichever
|
|
27
|
+
* target CDP happens to enumerate first.
|
|
23
28
|
*/
|
|
24
|
-
export declare function pickElectronTarget(browser: Browser,
|
|
29
|
+
export declare function pickElectronTarget(browser: Browser, options?: {
|
|
30
|
+
matcher?: string;
|
|
31
|
+
preferVisible?: boolean;
|
|
32
|
+
}): Promise<Page>;
|
|
25
33
|
/**
|
|
26
34
|
* SIGTERM the process tree, wait briefly, then SIGKILL anything still alive.
|
|
27
35
|
* Single-process variant for our own spawned children.
|
|
@@ -27,7 +27,26 @@ export interface LaunchHeadlessOptions {
|
|
|
27
27
|
/** Additional exact Puppeteer default arguments to suppress. */
|
|
28
28
|
ignoreDefaultArgs?: readonly string[];
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
/** Result of a headless Chromium launch. */
|
|
31
|
+
export interface LaunchHeadlessResult {
|
|
32
|
+
browser: Browser;
|
|
33
|
+
/**
|
|
34
|
+
* OMP-owned temporary Chromium profile directory to remove after the browser
|
|
35
|
+
* process tree exits, or `undefined` when the caller supplied its own
|
|
36
|
+
* `--user-data-dir` (which OMP must not delete).
|
|
37
|
+
*/
|
|
38
|
+
userDataDir?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promise<LaunchHeadlessResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Remove an OMP-owned headless Chromium profile directory, tolerating the brief
|
|
43
|
+
* window on Windows in which Chromium (or an orphaned browser subprocess) still
|
|
44
|
+
* holds the profile lock. The shared temp remover centralizes retry handling
|
|
45
|
+
* for EBUSY/EPERM/ENOTEMPTY; if the directory is still busy afterwards we warn
|
|
46
|
+
* and leave it for a later cleanup pass rather than throwing — a shutdown cleanup
|
|
47
|
+
* failure must never crash the process (issue #7058).
|
|
48
|
+
*/
|
|
49
|
+
export declare function removeUserDataDir(dir: string): Promise<void>;
|
|
31
50
|
export declare function applyViewport(page: Page, viewport?: {
|
|
32
51
|
width: number;
|
|
33
52
|
height: number;
|
|
@@ -25,6 +25,8 @@ export interface PuppeteerBrowserHandle extends BrowserHandleCommon {
|
|
|
25
25
|
browser: Browser;
|
|
26
26
|
cdpUrl?: string;
|
|
27
27
|
pid?: number;
|
|
28
|
+
/** OMP-owned temp Chromium profile directory removed on dispose (headless launches). */
|
|
29
|
+
userDataDir?: string;
|
|
28
30
|
subprocess?: Subprocess;
|
|
29
31
|
stealth: {
|
|
30
32
|
browserSession: CDPSession | null;
|
|
@@ -59,12 +59,20 @@ export type WorkerInitPayload = {
|
|
|
59
59
|
safeDir: string;
|
|
60
60
|
targetId: string;
|
|
61
61
|
dialogs?: "accept" | "dismiss";
|
|
62
|
+
url?: string;
|
|
63
|
+
waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
|
|
64
|
+
timeoutMs: number;
|
|
62
65
|
/**
|
|
63
66
|
* Post-timeout recycle: before adopting the page, dismiss any open JS dialog and
|
|
64
67
|
* stop a pending navigation so a blocked target cannot stall worker init (which
|
|
65
68
|
* previously force-killed the tab). Never set for first-time Electron attach.
|
|
66
69
|
*/
|
|
67
70
|
recover?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Whether the worker may raise this tab before capturing a screenshot. Unset
|
|
73
|
+
* behaves as `true`; the supervisor clears it for browsers we did not launch.
|
|
74
|
+
*/
|
|
75
|
+
activateForScreenshot?: boolean;
|
|
68
76
|
};
|
|
69
77
|
export type ToolReply = {
|
|
70
78
|
ok: true;
|
|
@@ -47,6 +47,7 @@ interface TabSessionBase<TBrowser extends BrowserHandle = BrowserHandle> {
|
|
|
47
47
|
export interface WorkerTabSession extends TabSessionBase<PuppeteerBrowserHandle> {
|
|
48
48
|
backend: "worker";
|
|
49
49
|
worker: WorkerHandle;
|
|
50
|
+
activateForScreenshot: boolean;
|
|
50
51
|
}
|
|
51
52
|
export interface CmuxTabSession extends TabSessionBase<CmuxBrowserHandle> {
|
|
52
53
|
backend: "cmux";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HTMLElement } from "linkedom";
|
|
2
|
-
import type { ElementHandle } from "puppeteer-core";
|
|
2
|
+
import type { ElementHandle, Page } from "puppeteer-core";
|
|
3
3
|
import type { Transport } from "./tab-protocol.js";
|
|
4
4
|
declare module "puppeteer-core" {
|
|
5
5
|
interface Frame {
|
|
@@ -15,6 +15,7 @@ declare global {
|
|
|
15
15
|
var innerHeight: number;
|
|
16
16
|
var document: {
|
|
17
17
|
elementFromPoint(x: number, y: number): Element | null;
|
|
18
|
+
readonly visibilityState: "visible" | "hidden";
|
|
18
19
|
};
|
|
19
20
|
}
|
|
20
21
|
export interface OpTimeouts {
|
|
@@ -66,6 +67,7 @@ export interface InflightOp {
|
|
|
66
67
|
}
|
|
67
68
|
/** Human-readable label for a screenshot op, used in op tracking + timeout errors. */
|
|
68
69
|
export declare function describeScreenshot(opts?: ScreenshotOptions): string;
|
|
70
|
+
export declare function preparePageForScreenshot(page: Pick<Page, "bringToFront" | "evaluate">, signal: AbortSignal | undefined, activate: boolean): Promise<void>;
|
|
69
71
|
/** Summarize still-running helpers (oldest first) so a cell timeout names what stalled. */
|
|
70
72
|
export declare function describeInflight(inflight: Map<number, InflightOp>): string;
|
|
71
73
|
export declare class WorkerCore {
|
|
@@ -12,3 +12,14 @@
|
|
|
12
12
|
* cannot find the structure they need.
|
|
13
13
|
*/
|
|
14
14
|
export declare function tokenizeShellSegments(command: string): string[][];
|
|
15
|
+
/**
|
|
16
|
+
* Returns the original text of flat shell command segments. Unlike
|
|
17
|
+
* `tokenizeShellSegments`, this preserves quoting and escaping so the results
|
|
18
|
+
* are safe to match against user-configured regular expressions.
|
|
19
|
+
*
|
|
20
|
+
* The extractor deliberately declines to split syntax whose execution context
|
|
21
|
+
* cannot be determined with this small scanner (heredocs, command substitution,
|
|
22
|
+
* backticks, grouping, and malformed quoting). Callers must still check the
|
|
23
|
+
* complete input in that case.
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractFlatShellCommandSegments(command: string): string[];
|
|
@@ -48,6 +48,8 @@ export declare class ImageInputTooLargeError extends Error {
|
|
|
48
48
|
readonly maxBytes: number;
|
|
49
49
|
constructor(bytes: number, maxBytes: number);
|
|
50
50
|
}
|
|
51
|
+
/** Converts an image to PNG, rejecting when the runtime cannot decode or encode it. */
|
|
52
|
+
export declare function convertImageToPng(image: ImageContent): Promise<ImageContent>;
|
|
51
53
|
export declare function ensureSupportedImageInput(image: ImageContent): Promise<ImageContent | null>;
|
|
52
54
|
export interface NormalizeModelContextImagesOptions {
|
|
53
55
|
/** Model the images are bound for; used to derive encoder constraints (WebP exclusion for Ollama). */
|
|
@@ -2,6 +2,15 @@ import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
|
2
2
|
import type { SearchResponse } from "../../../web/search/types.js";
|
|
3
3
|
import type { SearchParams } from "./base.js";
|
|
4
4
|
import { SearchProvider } from "./base.js";
|
|
5
|
+
/**
|
|
6
|
+
* Map a parsed `lang:` locale onto DuckDuckGo's documented `kl` values.
|
|
7
|
+
*
|
|
8
|
+
* Shared queries use `language-region` order while DDG generally uses
|
|
9
|
+
* `region-language`. Provider-specific exceptions resolve through
|
|
10
|
+
* {@link DDG_LOCALE_ALIASES}; all other values must survive the documented
|
|
11
|
+
* allowlist after swapping or the caller keeps its default region.
|
|
12
|
+
*/
|
|
13
|
+
export declare function localeToKl(lang: string | undefined): string | undefined;
|
|
5
14
|
/** Execute a DuckDuckGo web search via the no-JS HTML frontend. */
|
|
6
15
|
export declare function searchDuckDuckGo(params: SearchParams): Promise<SearchResponse>;
|
|
7
16
|
/** Search provider for DuckDuckGo (no API key required). */
|
|
@@ -26,7 +26,7 @@ export declare const SEARCH_PROVIDER_OPTIONS: readonly [{
|
|
|
26
26
|
}, {
|
|
27
27
|
readonly value: "xai";
|
|
28
28
|
readonly label: "xAI";
|
|
29
|
-
readonly description: "Grok web search via xAI Responses API (
|
|
29
|
+
readonly description: "Grok web search via xAI Responses API (uses SuperGrok/X Premium+ OAuth via /login xai-oauth, or XAI_API_KEY)";
|
|
30
30
|
}, {
|
|
31
31
|
readonly value: "zai";
|
|
32
32
|
readonly label: "Z.AI";
|
|
@@ -128,7 +128,7 @@ export declare const SEARCH_PROVIDER_CHOICES: ({
|
|
|
128
128
|
} | {
|
|
129
129
|
readonly value: "xai";
|
|
130
130
|
readonly label: "xAI";
|
|
131
|
-
readonly description: "Grok web search via xAI Responses API (
|
|
131
|
+
readonly description: "Grok web search via xAI Responses API (uses SuperGrok/X Premium+ OAuth via /login xai-oauth, or XAI_API_KEY)";
|
|
132
132
|
} | {
|
|
133
133
|
readonly value: "zai";
|
|
134
134
|
readonly label: "Z.AI";
|