@agentproto/runtime 4.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/app-ui-build.d.ts +96 -0
- package/dist/app-ui-build.mjs +250 -0
- package/dist/app-ui-build.mjs.map +1 -0
- package/dist/app-ui-placeholder.d.ts +73 -0
- package/dist/app-ui-placeholder.mjs +89 -0
- package/dist/app-ui-placeholder.mjs.map +1 -0
- package/dist/catalog-models.d.ts +1 -1
- package/dist/catalog-models.mjs.map +1 -1
- package/dist/config-schema.d.ts +1 -1
- package/dist/config-schema.mjs +179 -85
- package/dist/config-schema.mjs.map +1 -1
- package/dist/config.d.ts +39 -2
- package/dist/config.mjs +179 -85
- package/dist/config.mjs.map +1 -1
- package/dist/index.d.ts +1918 -215
- package/dist/index.mjs +7276 -965
- package/dist/index.mjs.map +1 -1
- package/dist/resume-strategies.mjs +7 -0
- package/dist/resume-strategies.mjs.map +1 -1
- package/dist/session-presence.mjs +179 -85
- package/dist/session-presence.mjs.map +1 -1
- package/dist/{spawn-defaults-CZj39Znq.d.ts → spawn-defaults-wQV9AeiG.d.ts} +12 -0
- package/dist/user-presets.d.ts +5 -0
- package/dist/user-presets.mjs +1 -0
- package/dist/user-presets.mjs.map +1 -1
- package/docs/TRANSMITTER.md +356 -0
- package/docs/mcp-tools/agent_start.md +494 -0
- package/docs/mcp-tools/app_run.md +47 -0
- package/package.json +28 -17
package/dist/index.d.ts
CHANGED
|
@@ -13,8 +13,8 @@ export { CostBudget, CostBudgetScope } from '@agentproto/auth';
|
|
|
13
13
|
import { S as SpawnBrowserMode, R as ResolveHeadlessBrowser, C as ContextContinuityPolicy, a as ResolvedContextContinuityPolicy } from './context-continuity-Bn99UtVC.js';
|
|
14
14
|
import { SandboxMode } from '@agentproto/command-sandbox';
|
|
15
15
|
import { AdapterHandle, SetupField, AdapterResolver, AdapterLister, CredsStore, HarnessCapabilities, AdapterEntry } from '@agentproto/provider-kit';
|
|
16
|
-
import { S as SpawnDefaultsConfig, D as DefaultsAdapterAuthConfig, R as ResolvedAuthSpec, a as DeclaredAdapterOption, A as AdapterAuthDescriptor } from './spawn-defaults-
|
|
17
|
-
export { b as AuthEcho, c as AuthResolutionError, C as CLAUDE_CODE_OAUTH_SOURCE, d as CredentialSource, e as DefaultsAdapterConfig, f as DeferredToolsConfig, g as ResolveSubscriptionCredentialInput, h as ResolvedSpawnAuthMaterial, i as ResolvedSpawnDefaults, j as SubscriptionCredentialResolution, k as SubscriptionSourceError, l as credentialFingerprint, n as normalizeSkillsOption, r as resolveAuthSpec, m as resolveDeferredToolsGatewayOption, o as resolveSpawnDefaults, p as resolveSubscriptionCredential } from './spawn-defaults-
|
|
16
|
+
import { S as SpawnDefaultsConfig, D as DefaultsAdapterAuthConfig, R as ResolvedAuthSpec, a as DeclaredAdapterOption, A as AdapterAuthDescriptor } from './spawn-defaults-wQV9AeiG.js';
|
|
17
|
+
export { b as AuthEcho, c as AuthResolutionError, C as CLAUDE_CODE_OAUTH_SOURCE, d as CredentialSource, e as DefaultsAdapterConfig, f as DeferredToolsConfig, g as ResolveSubscriptionCredentialInput, h as ResolvedSpawnAuthMaterial, i as ResolvedSpawnDefaults, j as SubscriptionCredentialResolution, k as SubscriptionSourceError, l as credentialFingerprint, n as normalizeSkillsOption, r as resolveAuthSpec, m as resolveDeferredToolsGatewayOption, o as resolveSpawnDefaults, p as resolveSubscriptionCredential } from './spawn-defaults-wQV9AeiG.js';
|
|
18
18
|
import { CatalogModelsQuery, CatalogModelsResponse } from './catalog-models.js';
|
|
19
19
|
export { CatalogAdapterInput, CatalogAdapterModelInput, CatalogPricing, CatalogProduct, CatalogRoute, CatalogRouteSummary, CatalogVendor, buildCatalogModels } from './catalog-models.js';
|
|
20
20
|
import { FooterSession } from './pr-provenance.js';
|
|
@@ -26,6 +26,7 @@ import * as _agentproto_tool from '@agentproto/tool';
|
|
|
26
26
|
import { ToolContext } from '@agentproto/tool';
|
|
27
27
|
import { DriverHandle } from '@agentproto/driver';
|
|
28
28
|
import { Attestation, LedgerKey, RubricDigest, ReviewPrRef, LaneResult, ReviewLaneExecutor, LaneOutcome } from '@agentproto/review';
|
|
29
|
+
import { ImportedMcpsConfig } from './mcp-imports.js';
|
|
29
30
|
import { UserPreset } from './user-presets.js';
|
|
30
31
|
export { UserPresetsFile, deleteUserPreset, getUserPreset, listUserPresets, loadUserPresets, saveUserPreset, userPresetsPath } from './user-presets.js';
|
|
31
32
|
import { WorktreeIsolationMode, SpawnAttachMode, SpawnDedupeMode } from './config.js';
|
|
@@ -451,13 +452,64 @@ type UsageSource = "adapter" | "computed" | "no-pricing" | "none";
|
|
|
451
452
|
interface TokenPricing {
|
|
452
453
|
inputPer1M: number;
|
|
453
454
|
outputPer1M: number;
|
|
455
|
+
/** Multiplier on `inputPer1M` for cache-read tokens (Anthropic: ~0.1).
|
|
456
|
+
* Absent = 1 (no discount), same default as the catalog. */
|
|
457
|
+
cacheReadMultiplier?: number;
|
|
458
|
+
/** Multiplier on `inputPer1M` for cache-creation tokens (Anthropic:
|
|
459
|
+
* ~1.25). Absent = 1. */
|
|
460
|
+
cacheWriteMultiplier?: number;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Token-detail and activity fields shared by every usage shape below. All
|
|
464
|
+
* optional and never zero-filled: absent means "this source doesn't report
|
|
465
|
+
* it", a present 0 is a measured zero.
|
|
466
|
+
*
|
|
467
|
+
* - `cacheReadTokens` / `cacheWriteTokens`: prompt-cache reads / cache
|
|
468
|
+
* creation, cumulative. Kept OUT of `tokensIn` (which is uncached input
|
|
469
|
+
* only when a cache split is known).
|
|
470
|
+
* - `reasoningTokens`: thinking/reasoning tokens, cumulative. A subset of
|
|
471
|
+
* `tokensOut` for providers that bill them as output (Anthropic).
|
|
472
|
+
* - `turns`: completed turns (the descriptor's `turnsCompleted`).
|
|
473
|
+
* - `toolCalls`: tool calls the daemon observed, cumulative.
|
|
474
|
+
* - `durationMs`: wall time spent inside turns, cumulative.
|
|
475
|
+
*/
|
|
476
|
+
interface UsageDetail {
|
|
477
|
+
cacheReadTokens?: number;
|
|
478
|
+
cacheWriteTokens?: number;
|
|
479
|
+
reasoningTokens?: number;
|
|
480
|
+
turns?: number;
|
|
481
|
+
toolCalls?: number;
|
|
482
|
+
durationMs?: number;
|
|
454
483
|
}
|
|
484
|
+
/** Copy only the present (`number`) `UsageDetail` fields of `src`. */
|
|
485
|
+
declare function pickUsageDetail(src: UsageDetail): UsageDetail;
|
|
455
486
|
/** Pluggable pricing accessor — defaults to the in-repo catalog's
|
|
456
487
|
* `resolvePricing`, overridable in tests. Returns undefined for an
|
|
457
488
|
* unknown model (the caller then tags `no-pricing`). */
|
|
458
489
|
type PricingResolver = (model: string) => TokenPricing | undefined;
|
|
490
|
+
/**
|
|
491
|
+
* What an adapter's usage reader (`readUsage`: hermes state.db, opencode.db,
|
|
492
|
+
* the claude-code transcript JSONL) returns. Every field is cumulative for
|
|
493
|
+
* the session and optional; `null` from the reader means "nothing found".
|
|
494
|
+
*/
|
|
495
|
+
interface UsageReadResult {
|
|
496
|
+
model?: string;
|
|
497
|
+
costUsd?: number;
|
|
498
|
+
tokensIn?: number;
|
|
499
|
+
tokensOut?: number;
|
|
500
|
+
cacheReadTokens?: number;
|
|
501
|
+
cacheWriteTokens?: number;
|
|
502
|
+
reasoningTokens?: number;
|
|
503
|
+
}
|
|
504
|
+
/** Where a reader should look for the session's native store — the spawn's
|
|
505
|
+
* cwd and isolated provider config dir (`CLAUDE_CONFIG_DIR` for
|
|
506
|
+
* claude-code). Readers that key only on the session id ignore it. */
|
|
507
|
+
interface UsageReadContext {
|
|
508
|
+
cwd?: string;
|
|
509
|
+
configDir?: string;
|
|
510
|
+
}
|
|
459
511
|
/** Raw signals gathered over a turn, handed to `deriveSessionUsage`. */
|
|
460
|
-
interface UsageComputeInput {
|
|
512
|
+
interface UsageComputeInput extends UsageDetail {
|
|
461
513
|
/** Requested model id — needed to look up per-token prices. */
|
|
462
514
|
model?: string;
|
|
463
515
|
/** Cost the adapter reported directly (readUsage or usage_update.cost).
|
|
@@ -471,7 +523,7 @@ interface UsageComputeInput {
|
|
|
471
523
|
/** The resolved usage snapshot — the shape `session_usage` returns and the
|
|
472
524
|
* durable `usage_snapshot` transcript record carries. Cost/token fields are
|
|
473
525
|
* omitted when absent so a missing value never reads as a measured zero. */
|
|
474
|
-
interface SessionUsage {
|
|
526
|
+
interface SessionUsage extends UsageDetail {
|
|
475
527
|
model?: string;
|
|
476
528
|
costUsd?: number;
|
|
477
529
|
tokensIn?: number;
|
|
@@ -488,7 +540,7 @@ interface SessionUsage {
|
|
|
488
540
|
*/
|
|
489
541
|
declare function deriveSessionUsage(input: UsageComputeInput, resolve?: PricingResolver): SessionUsage;
|
|
490
542
|
/** Descriptor-shaped fields `projectSessionUsage` reads. */
|
|
491
|
-
interface UsageDescriptorFields {
|
|
543
|
+
interface UsageDescriptorFields extends Omit<UsageDetail, "turns"> {
|
|
492
544
|
model?: string;
|
|
493
545
|
costUsd?: number;
|
|
494
546
|
tokensIn?: number;
|
|
@@ -496,6 +548,8 @@ interface UsageDescriptorFields {
|
|
|
496
548
|
contextSize?: number;
|
|
497
549
|
contextUsed?: number;
|
|
498
550
|
usageSource?: UsageSource;
|
|
551
|
+
/** Surfaced as `turns`. */
|
|
552
|
+
turnsCompleted?: number;
|
|
499
553
|
}
|
|
500
554
|
/**
|
|
501
555
|
* Project a session descriptor's persisted usage fields into the
|
|
@@ -830,7 +884,7 @@ declare function turnToActivities(session: ActivityTurnSession, opts?: {
|
|
|
830
884
|
interface ActivityRunStepSlice {
|
|
831
885
|
index: number;
|
|
832
886
|
label: string;
|
|
833
|
-
status: "pending" | "running" | "done" | "failed" | "skipped";
|
|
887
|
+
status: "pending" | "running" | "done" | "failed" | "skipped" | "cancelled";
|
|
834
888
|
sessionId?: string;
|
|
835
889
|
startedAt?: string;
|
|
836
890
|
endedAt?: string;
|
|
@@ -855,8 +909,8 @@ interface ActivityWorkflowRunSlice {
|
|
|
855
909
|
* Project one workflow run's steps (stage-barrier semantics): running →
|
|
856
910
|
* active; a `pending` step waits on the stage barrier — the unfinished
|
|
857
911
|
* steps of EARLIER stages are the sessions whose turn-ends open it;
|
|
858
|
-
* done/failed → terminal; skipped → cancelled; a step still
|
|
859
|
-
* a terminal run settles as cancelled.
|
|
912
|
+
* done/failed → terminal; skipped/cancelled → cancelled; a step still
|
|
913
|
+
* pending inside a terminal run settles as cancelled.
|
|
860
914
|
*/
|
|
861
915
|
declare function workflowToActivities(run: ActivityWorkflowRunSlice): ActivityRecord[];
|
|
862
916
|
/**
|
|
@@ -1736,7 +1790,7 @@ declare function createTaskLedger(opts: {
|
|
|
1736
1790
|
* list — {@link isKnownSessionEndReason} says so, and callers should fall
|
|
1737
1791
|
* back to a generic label rather than throw.
|
|
1738
1792
|
*/
|
|
1739
|
-
declare const SESSION_END_REASONS: readonly ["daemon-restart", "idle-reaped", "crashed", "operator-completed", "operator-stopped", "cost-cap-exceeded", "policy-cleanup", "parent-exited", "provider-limit", "forgotten"];
|
|
1793
|
+
declare const SESSION_END_REASONS: readonly ["daemon-restart", "idle-reaped", "crashed", "operator-completed", "operator-stopped", "cost-cap-exceeded", "policy-cleanup", "parent-exited", "provider-limit", "forgotten", "steward-completed", "steward-abandoned"];
|
|
1740
1794
|
type SessionEndReason = (typeof SESSION_END_REASONS)[number];
|
|
1741
1795
|
/** True when `reason` is one of {@link SESSION_END_REASONS}. A label
|
|
1742
1796
|
* lookup, not a wire-format gate — an unrecognized string is still a
|
|
@@ -1763,7 +1817,7 @@ declare function isProviderLimitError(message: string | undefined): boolean;
|
|
|
1763
1817
|
* and session_monitor MCP tool (long-poll multiplexed).
|
|
1764
1818
|
*/
|
|
1765
1819
|
|
|
1766
|
-
type SessionEventType = "session:turn-end" | "session:awaiting-input" | "session:awaiting-input-flagged" | "session:awaiting-question-answered" | "session:permission-request" | "session:permission-resolved" | "session:exited" | "session:reaped" | "session:stalled" | "session:stall-cleared" | "session:watcher-attached" | "session:watcher-detached" | "session:bg-tasks-parked" | "session:bg-tasks-cleared" | "session:bg-task" | "session:resumed" | "session:spawned" | "session:command-done" | "session:model-changed" | "session:config-changed" | "session:renamed" | "session:pinned-changed" | "session:message" | "policy:passed" | "policy:failed" | "policy:commit-ready" | "policy:committed" | "cron:fired" | "cron:succeeded" | "cron:failed" | "activity:changed" | "task:changed" | "workflow:gate-report" | "workflow:suspended" | "workflow:suspend-resumed" | "session:harness-warning";
|
|
1820
|
+
type SessionEventType = "session:turn-end" | "session:awaiting-input" | "session:awaiting-input-flagged" | "session:awaiting-question-answered" | "session:permission-request" | "session:permission-resolved" | "session:exited" | "session:reaped" | "session:stalled" | "session:stall-cleared" | "session:watcher-attached" | "session:watcher-detached" | "session:bg-tasks-parked" | "session:bg-tasks-cleared" | "session:bg-task" | "session:resumed" | "session:spawned" | "session:command-done" | "session:model-changed" | "session:config-changed" | "session:renamed" | "session:pinned-changed" | "session:artifact-added" | "session:artifact-pinned-changed" | "session:message" | "policy:passed" | "policy:failed" | "policy:commit-ready" | "policy:committed" | "cron:fired" | "cron:succeeded" | "cron:failed" | "activity:changed" | "task:changed" | "workflow:gate-report" | "workflow:suspended" | "workflow:suspend-resumed" | "session:harness-warning";
|
|
1767
1821
|
/**
|
|
1768
1822
|
* Fixed severity vocabulary for a judge-gate finding (WP-D). Deliberately
|
|
1769
1823
|
* small and fixed so `policy_status` output is comparable across different
|
|
@@ -1837,6 +1891,18 @@ interface SessionTurnEndEvent {
|
|
|
1837
1891
|
* a normal, productive turn.
|
|
1838
1892
|
*/
|
|
1839
1893
|
empty?: boolean;
|
|
1894
|
+
/**
|
|
1895
|
+
* The in-band `error` stream event's message text observed during this
|
|
1896
|
+
* turn, when one occurred (see `SessionDescriptor.lastTurnErrorMessage`'s
|
|
1897
|
+
* doc). Present whenever `reason` is `"error"` AND the adapter emitted an
|
|
1898
|
+
* `error`-kind event before ending the stream — including the case where
|
|
1899
|
+
* the generator simply returned with no explicit turn-end and no thrown
|
|
1900
|
+
* exception, which is exactly what left an errored turn indistinguishable
|
|
1901
|
+
* from a clean one before this field existed. Absent when the adapter
|
|
1902
|
+
* reported `reason:"error"` on its own turn-end without a preceding
|
|
1903
|
+
* `error` event, and absent (not empty string) on every non-error turn.
|
|
1904
|
+
*/
|
|
1905
|
+
error?: string;
|
|
1840
1906
|
/**
|
|
1841
1907
|
* True when the turn was not started by a prompt: the agent woke on its
|
|
1842
1908
|
* own (Claude Code's task-notification cycle after a background task
|
|
@@ -2299,6 +2365,32 @@ interface SessionPinnedEvent {
|
|
|
2299
2365
|
pinned: boolean;
|
|
2300
2366
|
ts: string;
|
|
2301
2367
|
}
|
|
2368
|
+
/**
|
|
2369
|
+
* Emitted when a new artifact (or a new version of an existing one) is
|
|
2370
|
+
* materialized into the session's artifact store (`session_artifact_add`,
|
|
2371
|
+
* `POST /sessions/:id/artifacts` — see `session-artifacts.ts`). Carries just
|
|
2372
|
+
* enough for a live UI to append/update its inline card and "Artifacts"
|
|
2373
|
+
* section without a round trip — the full record (all versions) is a
|
|
2374
|
+
* `session_artifact_list` / `GET /sessions/:id/artifacts` call away.
|
|
2375
|
+
*/
|
|
2376
|
+
interface SessionArtifactAddedEvent {
|
|
2377
|
+
type: "session:artifact-added";
|
|
2378
|
+
sessionId: string;
|
|
2379
|
+
key: string;
|
|
2380
|
+
kind: string;
|
|
2381
|
+
version: number;
|
|
2382
|
+
label?: string;
|
|
2383
|
+
ts: string;
|
|
2384
|
+
}
|
|
2385
|
+
/** Emitted when an artifact's `pinned` flag changes
|
|
2386
|
+
* (`session_artifact_pin`, `POST /sessions/:id/artifacts/:key/pin`). */
|
|
2387
|
+
interface SessionArtifactPinnedEvent {
|
|
2388
|
+
type: "session:artifact-pinned-changed";
|
|
2389
|
+
sessionId: string;
|
|
2390
|
+
key: string;
|
|
2391
|
+
pinned: boolean;
|
|
2392
|
+
ts: string;
|
|
2393
|
+
}
|
|
2302
2394
|
/**
|
|
2303
2395
|
* Emitted when a session is first registered in the registry (WP-R3) — both
|
|
2304
2396
|
* the agent-cli spawn path (`spawnAgent`) and the terminal path (`spawnPty`).
|
|
@@ -2517,7 +2609,7 @@ interface SessionHarnessWarningEvent {
|
|
|
2517
2609
|
label?: string;
|
|
2518
2610
|
ts: string;
|
|
2519
2611
|
}
|
|
2520
|
-
type SessionEvent = SessionTurnEndEvent | SessionAwaitingInputEvent | SessionAwaitingInputFlaggedEvent | SessionAwaitingQuestionAnsweredEvent | SessionPermissionRequestEvent | SessionPermissionResolvedEvent | SessionExitedEvent | SessionReapedEvent | SessionStalledEvent | SessionStallClearedEvent | SessionWatcherAttachedEvent | SessionWatcherDetachedEvent | SessionBgTasksParkedEvent | SessionBgTasksClearedEvent | SessionBgTaskEvent | SessionResumedEvent | SessionSpawnedEvent | SessionCommandDoneEvent | SessionModelChangedEvent | SessionConfigChangedEvent | SessionRenamedEvent | SessionPinnedEvent | SessionMessageEvent | PolicyPassedEvent | PolicyFailedEvent | PolicyCommitReadyEvent | PolicyCommittedEvent | CronFiredEvent | CronSucceededEvent | CronFailedEvent | ActivityChangedEvent | TaskChangedEvent | WorkflowGateReportEvent | WorkflowApprovalRequestedEvent | WorkflowApprovalResolvedEvent | WorkflowSuspendedEvent | WorkflowSuspendResumedEvent | SessionHarnessWarningEvent;
|
|
2612
|
+
type SessionEvent = SessionTurnEndEvent | SessionAwaitingInputEvent | SessionAwaitingInputFlaggedEvent | SessionAwaitingQuestionAnsweredEvent | SessionPermissionRequestEvent | SessionPermissionResolvedEvent | SessionExitedEvent | SessionReapedEvent | SessionStalledEvent | SessionStallClearedEvent | SessionWatcherAttachedEvent | SessionWatcherDetachedEvent | SessionBgTasksParkedEvent | SessionBgTasksClearedEvent | SessionBgTaskEvent | SessionResumedEvent | SessionSpawnedEvent | SessionCommandDoneEvent | SessionModelChangedEvent | SessionConfigChangedEvent | SessionRenamedEvent | SessionPinnedEvent | SessionArtifactAddedEvent | SessionArtifactPinnedEvent | SessionMessageEvent | PolicyPassedEvent | PolicyFailedEvent | PolicyCommitReadyEvent | PolicyCommittedEvent | CronFiredEvent | CronSucceededEvent | CronFailedEvent | ActivityChangedEvent | TaskChangedEvent | WorkflowGateReportEvent | WorkflowApprovalRequestedEvent | WorkflowApprovalResolvedEvent | WorkflowSuspendedEvent | WorkflowSuspendResumedEvent | SessionHarnessWarningEvent;
|
|
2521
2613
|
interface SessionEventBus {
|
|
2522
2614
|
emit(ev: SessionEvent): void;
|
|
2523
2615
|
/** Subscribe to a specific event type. Returns an unsubscribe fn. */
|
|
@@ -2763,6 +2855,318 @@ declare class TunnelRegistry {
|
|
|
2763
2855
|
private loadFromDisk;
|
|
2764
2856
|
}
|
|
2765
2857
|
|
|
2858
|
+
/**
|
|
2859
|
+
* Typed inter-session message envelope (AIP-46 §Session messages).
|
|
2860
|
+
*
|
|
2861
|
+
* A PROMPT is an instruction from whoever drives a session; a MESSAGE is a
|
|
2862
|
+
* report between sessions in the same tree whose sender the DAEMON attests.
|
|
2863
|
+
* Everything in `from` is computed here from the caller's verified identity
|
|
2864
|
+
* and the session forest — a send request can never set it.
|
|
2865
|
+
*
|
|
2866
|
+
* Pure module: no registry, no I/O. The registry (sessions.ts) carries a
|
|
2867
|
+
* `SessionMessage` on a queued prompt (`QueuedPrompt.envelope`), renders it
|
|
2868
|
+
* into the model's context with `renderSessionMessages`, and records it as
|
|
2869
|
+
* its own `session-message` transcript record — never a `user-prompt`.
|
|
2870
|
+
*/
|
|
2871
|
+
/** Sender's position in the tree, relative to the recipient. */
|
|
2872
|
+
type MessageRelation = "child" | "parent" | "sibling" | "human" | "system";
|
|
2873
|
+
type MessageKind = "report" | "question" | "blocker" | "done" | "notice";
|
|
2874
|
+
type MessageUrgency = "fyi" | "next-turn" | "steer" | "interrupt";
|
|
2875
|
+
type MessageDeliveryVia = "wait" | "steer" | "turn" | "interrupt" | "inbox";
|
|
2876
|
+
declare const MESSAGE_KINDS: readonly MessageKind[];
|
|
2877
|
+
declare const MESSAGE_URGENCIES: readonly MessageUrgency[];
|
|
2878
|
+
interface SessionMessageFrom {
|
|
2879
|
+
/** Absent only for relation `human` | `system`. */
|
|
2880
|
+
sessionId?: string;
|
|
2881
|
+
/** Registry label at send time — display only, never identity. */
|
|
2882
|
+
label?: string;
|
|
2883
|
+
role?: string;
|
|
2884
|
+
adapter?: string;
|
|
2885
|
+
relation: MessageRelation;
|
|
2886
|
+
}
|
|
2887
|
+
interface SessionMessage {
|
|
2888
|
+
/** `msg_<uuid8>`, daemon-minted. */
|
|
2889
|
+
id: string;
|
|
2890
|
+
ts: string;
|
|
2891
|
+
/** Recipient sessionId. */
|
|
2892
|
+
to: string;
|
|
2893
|
+
from: SessionMessageFrom;
|
|
2894
|
+
kind: MessageKind;
|
|
2895
|
+
/** Requested urgency. */
|
|
2896
|
+
urgency: MessageUrgency;
|
|
2897
|
+
/** Stamped on delivery. */
|
|
2898
|
+
delivered?: {
|
|
2899
|
+
via: MessageDeliveryVia;
|
|
2900
|
+
at: string;
|
|
2901
|
+
turnSeq?: number;
|
|
2902
|
+
};
|
|
2903
|
+
/** Thread id; defaults to the first message's id. */
|
|
2904
|
+
correlationId?: string;
|
|
2905
|
+
/** A `SessionMessage.id` this answers. */
|
|
2906
|
+
replyTo?: string;
|
|
2907
|
+
text: string;
|
|
2908
|
+
/** Optional structured payload (≤ `MAX_MESSAGE_DATA_BYTES`). */
|
|
2909
|
+
data?: Record<string, unknown>;
|
|
2910
|
+
ackedAt?: string;
|
|
2911
|
+
}
|
|
2912
|
+
declare const MAX_MESSAGE_DATA_BYTES: number;
|
|
2913
|
+
/** The slice of a session descriptor relation/ACL resolution needs. */
|
|
2914
|
+
interface MessageTreeNode {
|
|
2915
|
+
id: string;
|
|
2916
|
+
parentSessionId?: string;
|
|
2917
|
+
label?: string;
|
|
2918
|
+
role?: string;
|
|
2919
|
+
adapterSlug?: string;
|
|
2920
|
+
}
|
|
2921
|
+
/**
|
|
2922
|
+
* Relation of `sender` to `recipient` in the session forest, or `undefined`
|
|
2923
|
+
* when they're not tree neighbours (cousins, strangers, self). `sender`
|
|
2924
|
+
* absent ⇒ `human` (an HTTP/CLI operator); daemon-originated notices pass
|
|
2925
|
+
* `"system"` explicitly instead of calling this.
|
|
2926
|
+
*/
|
|
2927
|
+
declare function resolveRelation(sender: MessageTreeNode | undefined, recipient: MessageTreeNode): Exclude<MessageRelation, "system"> | undefined;
|
|
2928
|
+
/**
|
|
2929
|
+
* ACL: child→parent, parent→direct child, human/system→any. Sibling↔sibling
|
|
2930
|
+
* only when `allowSiblings` (config-gated, default off). Anything else — a
|
|
2931
|
+
* cousin, a stranger, yourself — is refused.
|
|
2932
|
+
*/
|
|
2933
|
+
declare function isMessageAllowed(relation: MessageRelation | undefined, opts?: {
|
|
2934
|
+
allowSiblings?: boolean;
|
|
2935
|
+
}): relation is MessageRelation;
|
|
2936
|
+
/** Build a `from` block from the verified sender + the resolved relation. */
|
|
2937
|
+
declare function messageFrom(sender: MessageTreeNode | undefined, relation: MessageRelation): SessionMessageFrom;
|
|
2938
|
+
declare function mintMessageId(): string;
|
|
2939
|
+
declare function createSessionMessage(input: {
|
|
2940
|
+
to: string;
|
|
2941
|
+
from: SessionMessageFrom;
|
|
2942
|
+
text: string;
|
|
2943
|
+
kind?: MessageKind;
|
|
2944
|
+
urgency?: MessageUrgency;
|
|
2945
|
+
replyTo?: string;
|
|
2946
|
+
correlationId?: string;
|
|
2947
|
+
data?: Record<string, unknown>;
|
|
2948
|
+
}): SessionMessage;
|
|
2949
|
+
interface MessageFilter {
|
|
2950
|
+
/** Sender session ids, or `"children"` for any message whose relation is
|
|
2951
|
+
* `child` (i.e. from one of the recipient's own children). */
|
|
2952
|
+
from?: readonly string[] | "children";
|
|
2953
|
+
kind?: readonly MessageKind[];
|
|
2954
|
+
correlationId?: string;
|
|
2955
|
+
}
|
|
2956
|
+
declare function matchesMessageFilter(msg: SessionMessage, filter: MessageFilter): boolean;
|
|
2957
|
+
declare const MESSAGE_TAG = "agentproto-message";
|
|
2958
|
+
/** Child-settable label → attribute-safe: `[A-Za-z0-9._-]`, ≤ 48 chars. */
|
|
2959
|
+
declare function sanitizeLabel(label: string | undefined): string | undefined;
|
|
2960
|
+
/** Escape every envelope/body sentinel inside a message body, so a sender
|
|
2961
|
+
* can't close the tag early and forge a second header. */
|
|
2962
|
+
declare function escapeMessageBody(text: string): string;
|
|
2963
|
+
/** A human prompt is NOT wrapped (humans keep today's UX), but a line that
|
|
2964
|
+
* starts with the envelope sentinel is escaped, so the invariant "text
|
|
2965
|
+
* outside an `<agentproto-message>` tag is the human" holds. Nothing else
|
|
2966
|
+
* in human text changes. */
|
|
2967
|
+
declare function escapeHumanPrompt(text: string): string;
|
|
2968
|
+
declare function renderSessionMessage(msg: SessionMessage): string;
|
|
2969
|
+
/** A coalesced batch → sibling tags, one per message, in order. */
|
|
2970
|
+
declare function renderSessionMessages(msgs: readonly SessionMessage[]): string;
|
|
2971
|
+
/** One-line typed digest of `fyi` messages that never woke the session —
|
|
2972
|
+
* prepended (as a `system-prompt` slice) to the next turn it runs anyway. */
|
|
2973
|
+
declare function renderInboxDigest(msgs: readonly SessionMessage[]): string;
|
|
2974
|
+
/** Taught once, the first time a session RECEIVES a message (recorded as a
|
|
2975
|
+
* `system-prompt`). Children also get the same invariant in their lineage
|
|
2976
|
+
* line at spawn. */
|
|
2977
|
+
declare const MESSAGE_PREAMBLE: string;
|
|
2978
|
+
|
|
2979
|
+
/**
|
|
2980
|
+
* Shared types for pluggable sentinel providers (AIP-60 §3, §4).
|
|
2981
|
+
*
|
|
2982
|
+
* The sentinel family is a `@agentproto/provider-kit` consumer, same shape as
|
|
2983
|
+
* the tunnel family (`remote-providers/types.ts`) and the sandbox family
|
|
2984
|
+
* (`sandbox-providers/types.ts`): {@link SentinelProviderHandle} extends the
|
|
2985
|
+
* kit's generic `AdapterHandle` (slug/name/version/description/requiresSetup/
|
|
2986
|
+
* check) with the sentinel-specific `capabilities` plus the create/attach/
|
|
2987
|
+
* cancel/status/poll/ack/parseInbound lifecycle a provider implements.
|
|
2988
|
+
*
|
|
2989
|
+
* This file is deliberately self-contained (no import from `sentinel-store.js`)
|
|
2990
|
+
* so the contract never depends on the store's internals — `SentinelSpec` is
|
|
2991
|
+
* the vocabulary a provider is handed, `sentinel-store.ts` imports it from
|
|
2992
|
+
* here rather than the other way around.
|
|
2993
|
+
*/
|
|
2994
|
+
|
|
2995
|
+
/** When a sentinel stops watching. */
|
|
2996
|
+
type SentinelUntil = {
|
|
2997
|
+
kind: "subject_terminal";
|
|
2998
|
+
} | {
|
|
2999
|
+
kind: "at";
|
|
3000
|
+
ms: number;
|
|
3001
|
+
} | {
|
|
3002
|
+
kind: "count";
|
|
3003
|
+
n: number;
|
|
3004
|
+
} | {
|
|
3005
|
+
kind: "never";
|
|
3006
|
+
};
|
|
3007
|
+
/** Where a matching event is delivered.
|
|
3008
|
+
*
|
|
3009
|
+
* `routine` is reserved for AIP-41 `schedule.kind: event` binding; `webhook`
|
|
3010
|
+
* is reserved for forwarding a matched event to an external HTTP callback.
|
|
3011
|
+
* Both are FROZEN SHAPES ONLY — `SentinelStore.create` rejects them with
|
|
3012
|
+
* {@link SentinelTargetNotImplementedError} until a later step wires actual
|
|
3013
|
+
* delivery (design §6/§12 step 4 for `routine`; `webhook` has no design
|
|
3014
|
+
* section yet). Declaring the shape now lets callers/tools reference the
|
|
3015
|
+
* full union before delivery exists. */
|
|
3016
|
+
type SentinelTarget = {
|
|
3017
|
+
kind: "session";
|
|
3018
|
+
sessionId: string;
|
|
3019
|
+
urgency: MessageUrgency;
|
|
3020
|
+
} | {
|
|
3021
|
+
kind: "routine";
|
|
3022
|
+
routineId: string;
|
|
3023
|
+
} | {
|
|
3024
|
+
kind: "webhook";
|
|
3025
|
+
url: string;
|
|
3026
|
+
secret: string;
|
|
3027
|
+
};
|
|
3028
|
+
/** One clause of a sentinel's watch: a subject (exact, or a `*`-suffixed
|
|
3029
|
+
* prefix match against an event's `subjects`) plus an optional per-clause
|
|
3030
|
+
* type glob list. */
|
|
3031
|
+
interface SentinelMatchClause {
|
|
3032
|
+
/** Hierarchical routing key, e.g. "github:agentproto/ts#1428". A trailing
|
|
3033
|
+
* `*` is a prefix match against an event's `subjects`. */
|
|
3034
|
+
subject: string;
|
|
3035
|
+
/** Type globs; undefined = the provider's `defaultTypes(subject)`. */
|
|
3036
|
+
types?: string[];
|
|
3037
|
+
}
|
|
3038
|
+
/**
|
|
3039
|
+
* "Watch any of `match` until `condition`, deliver to `target`."
|
|
3040
|
+
*
|
|
3041
|
+
* `match` is OR semantics: an event matches the sentinel if it matches ANY
|
|
3042
|
+
* clause (subject + that clause's own type glob / provider default). At
|
|
3043
|
+
* least one clause is required — use {@link singleMatch} for a one-clause
|
|
3044
|
+
* spec. The event's own `subject` (not the matching clause's, which may be a
|
|
3045
|
+
* `*` prefix template) is what lands as `correlationId: sentinel:<subject>`
|
|
3046
|
+
* and the `[<scheme>]` inbox-text prefix — see `sentinel-runtime.ts`.
|
|
3047
|
+
*
|
|
3048
|
+
* Dedup key is `(sentinelId, event.id)`, same for every clause — see
|
|
3049
|
+
* {@link SentinelEvent.id}.
|
|
3050
|
+
*/
|
|
3051
|
+
interface SentinelSpec {
|
|
3052
|
+
match: SentinelMatchClause[];
|
|
3053
|
+
until: SentinelUntil;
|
|
3054
|
+
target: SentinelTarget;
|
|
3055
|
+
/** Provider slug; undefined = auto-select (not implemented by the runtime
|
|
3056
|
+
* yet — step 2 requires an explicit slug). */
|
|
3057
|
+
provider?: string;
|
|
3058
|
+
/** Fan-out grouping — sentinels created together (e.g. several
|
|
3059
|
+
* PR-related watches for one review) share a `group` id so they can be
|
|
3060
|
+
* listed/removed together later. Purely descriptive: the runtime never
|
|
3061
|
+
* reads it, no MCP tool consumes it yet. */
|
|
3062
|
+
group?: string;
|
|
3063
|
+
/** Human-readable label surfaced by `sentinel_list` / the CLI. Purely
|
|
3064
|
+
* descriptive, same as `group`. */
|
|
3065
|
+
label?: string;
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
/**
|
|
3069
|
+
* SentinelStore — persists `~/.agentproto/sentinels.json` (AIP-60 §2).
|
|
3070
|
+
*
|
|
3071
|
+
* State is in-memory; sentinels persist to disk (debounced async write +
|
|
3072
|
+
* sync flush at shutdown), mirroring `inbound-endpoints.ts`'s
|
|
3073
|
+
* `InboundEndpointStore`. Unlike that store's `markSeen` (deliberately
|
|
3074
|
+
* in-memory only), dedup here is PERSISTED per sentinel, bounded to the last
|
|
3075
|
+
* 1000 event ids (design §2/§9) — a sentinel's dedup window must survive a
|
|
3076
|
+
* daemon restart, since the whole point of a sentinel is to keep watching
|
|
3077
|
+
* across restarts.
|
|
3078
|
+
*
|
|
3079
|
+
* Writes are atomic (write to a per-process tmp file, then rename), same
|
|
3080
|
+
* idiom as `sandbox-ledger.ts` — a crash mid-write must never leave a
|
|
3081
|
+
* corrupt `sentinels.json` behind.
|
|
3082
|
+
*/
|
|
3083
|
+
|
|
3084
|
+
type SentinelStatus = "active" | "paused" | "expired" | "orphaned" | "error";
|
|
3085
|
+
|
|
3086
|
+
/**
|
|
3087
|
+
* LlmEndpointRegistry — supervised-sidecar manager for the
|
|
3088
|
+
* `@agentproto/llm-endpoint` proxy gateway.
|
|
3089
|
+
*
|
|
3090
|
+
* The daemon spawns the llm-endpoint bin as a CHILD PROCESS (never an
|
|
3091
|
+
* in-process import — the runtime package has zero dependency on
|
|
3092
|
+
* @agentproto/llm-endpoint, exactly like it never imports cloudflared).
|
|
3093
|
+
* This is the single-sidecar analog of {@link TunnelRegistry}: one endpoint,
|
|
3094
|
+
* one port, an in-memory descriptor that tracks pid / port / status /
|
|
3095
|
+
* startedAt, and a start / stop / status lifecycle exposed over MCP.
|
|
3096
|
+
*
|
|
3097
|
+
* Lifecycle (mirrors RemoteController's singleton style — created fresh each
|
|
3098
|
+
* boot, state in memory; the child dies with the daemon so there is nothing
|
|
3099
|
+
* to reconcile on restart):
|
|
3100
|
+
* start(input) → assemble env, spawn `node <bin> serve`, health-probe to
|
|
3101
|
+
* readiness, store descriptor. Idempotent: a call while the
|
|
3102
|
+
* endpoint is already running+healthy returns the existing
|
|
3103
|
+
* descriptor without spawning (like `start_browser`).
|
|
3104
|
+
* status() → descriptor + live `GET /v1/models` health probe.
|
|
3105
|
+
* stop() → SIGTERM the child, mark stopped (mirrors tunnel stop).
|
|
3106
|
+
* shutdown() → stop the child on daemon exit.
|
|
3107
|
+
*
|
|
3108
|
+
* Env on spawn (see {@link assembleLlmEndpointEnv}): the provider API keys
|
|
3109
|
+
* via `injectProviderKeysIntoEnv` (the same store llm-endpoint's own cli.ts
|
|
3110
|
+
* reads), `LLM_ENDPOINT_PORT` (default 18090), and `LLM_ENDPOINT_ACCESS_TOKENS`
|
|
3111
|
+
* when configured. Explicit env passed to `start` always wins.
|
|
3112
|
+
*/
|
|
3113
|
+
type LlmEndpointStatus = "starting" | "running" | "stopped" | "error";
|
|
3114
|
+
/** The `llm_endpoint_status` MCP contract — see {@link LlmEndpointRegistry.status}. */
|
|
3115
|
+
interface LlmEndpointStatusReport {
|
|
3116
|
+
running: boolean;
|
|
3117
|
+
pid: number | null;
|
|
3118
|
+
port: number | null;
|
|
3119
|
+
baseUrl: string | null;
|
|
3120
|
+
healthy: boolean;
|
|
3121
|
+
startedAt: string | null;
|
|
3122
|
+
status: LlmEndpointStatus | "never-started";
|
|
3123
|
+
/** Who is actually serving this endpoint. `"daemon"` — this registry's own
|
|
3124
|
+
* spawn (or nothing at all). `"external"` — a healthy process on the same
|
|
3125
|
+
* port that this registry did NOT spawn (e.g. a launchd KeepAlive
|
|
3126
|
+
* sidecar); `pid` is unknown (always `null`) and `injectedProviders`, if
|
|
3127
|
+
* present, was DERIVED by probing `/v1/models` rather than read off our
|
|
3128
|
+
* own key-injection bookkeeping. */
|
|
3129
|
+
owner: "daemon" | "external";
|
|
3130
|
+
/** Whether upstream links persisted via `llm_endpoint_set_upstream_link`
|
|
3131
|
+
* are actually applied to the process answering this port. Always
|
|
3132
|
+
* `false` for `owner:"external"` — this registry never spawned it, so it
|
|
3133
|
+
* never injected `LLM_ENDPOINT_PROFILE_<UPSTREAM>`; the operator must
|
|
3134
|
+
* configure the external service's own env directly. */
|
|
3135
|
+
linksApplied: boolean;
|
|
3136
|
+
lastError?: string;
|
|
3137
|
+
injectedProviders?: string[];
|
|
3138
|
+
linkedProviders?: string[];
|
|
3139
|
+
/** `@agentproto/llm-endpoint`'s own package version, read from the
|
|
3140
|
+
* resolved bin's `package.json` — best-effort, present only for
|
|
3141
|
+
* `owner:"daemon"` (the version of the code this registry would spawn /
|
|
3142
|
+
* did spawn). Undefined when unresolvable, or for `owner:"external"`
|
|
3143
|
+
* (an adopted process exposes no version of its own to probe). */
|
|
3144
|
+
version?: string;
|
|
3145
|
+
}
|
|
3146
|
+
interface LlmEndpointDescriptor {
|
|
3147
|
+
pid: number | null;
|
|
3148
|
+
port: number;
|
|
3149
|
+
baseUrl: string;
|
|
3150
|
+
status: LlmEndpointStatus;
|
|
3151
|
+
startedAt: string;
|
|
3152
|
+
stoppedAt?: string;
|
|
3153
|
+
lastError?: string;
|
|
3154
|
+
/** Provider names whose keys were injected into the child's env (never values). */
|
|
3155
|
+
injectedProviders?: string[];
|
|
3156
|
+
/** Upstream names mapped to an auth-profile link (`LLM_ENDPOINT_PROFILE_<P>`)
|
|
3157
|
+
* in the child's env. Profile IDS, not secrets — but only the provider names
|
|
3158
|
+
* are surfaced here, mirroring `injectedProviders`. */
|
|
3159
|
+
linkedProviders?: string[];
|
|
3160
|
+
/**
|
|
3161
|
+
* Only populated on the value returned by {@link LlmEndpointRegistry.start}
|
|
3162
|
+
* (never stored on the in-memory descriptor): `true` when the call reused an
|
|
3163
|
+
* already-running/starting child instead of spawning — so the caller can
|
|
3164
|
+
* tell a fresh `start` (new port/tokens applied) from a no-op idempotent hit
|
|
3165
|
+
* (old descriptor returned, nothing new applied). Mirrors `start_browser`.
|
|
3166
|
+
*/
|
|
3167
|
+
wasAlreadyRunning?: boolean;
|
|
3168
|
+
}
|
|
3169
|
+
|
|
2766
3170
|
/**
|
|
2767
3171
|
* RemoteController — owns the gateway's "publish to the internet" state.
|
|
2768
3172
|
*
|
|
@@ -2970,6 +3374,14 @@ interface PairingRecord {
|
|
|
2970
3374
|
/** Set on a pairing made under the retired pair/v1 protocol: listed and
|
|
2971
3375
|
* revocable, never served — its client is told to re-pair. */
|
|
2972
3376
|
legacy?: true;
|
|
3377
|
+
/** Set when this pairing was made under an offer minted with
|
|
3378
|
+
* `agentproto pair offer --host` (PR-C) — i.e. the offering daemon's own
|
|
3379
|
+
* intent, recorded server-side at handshake time from the OfferEntry it
|
|
3380
|
+
* itself created, never from the (unauthenticated) `scope` param a client
|
|
3381
|
+
* presented in the offer URL. Absent for an ordinary remote-control
|
|
3382
|
+
* pairing. Purely informational here — it does not change how this
|
|
3383
|
+
* pairing is served. */
|
|
3384
|
+
scope?: "host";
|
|
2973
3385
|
}
|
|
2974
3386
|
/** Mode of a served channel — first-contact offer vs. an established reconnect. */
|
|
2975
3387
|
type PairingChannelMode = "offer" | "reconnect";
|
|
@@ -2981,6 +3393,13 @@ interface PairingChannelContext {
|
|
|
2981
3393
|
fingerprint: string;
|
|
2982
3394
|
/** Client label. */
|
|
2983
3395
|
name: string;
|
|
3396
|
+
/** This pairing's OWN recorded scope (mirrors `PairingRecord.scope`) —
|
|
3397
|
+
* `"host"` when the offer that produced it was minted with
|
|
3398
|
+
* `agentproto pair offer --host`, absent for an ordinary remote-control
|
|
3399
|
+
* pairing. Present on BOTH the offer and every reconnect context, so
|
|
3400
|
+
* `serve` can gate scope-restricted routes (e.g. device-inference
|
|
3401
|
+
* proxying) the same way on first contact and on every later reconnect. */
|
|
3402
|
+
scope?: "host";
|
|
2984
3403
|
}
|
|
2985
3404
|
/** Handle to a live served channel — the registry closes it on teardown. */
|
|
2986
3405
|
interface PairingChannelHandle {
|
|
@@ -3003,12 +3422,21 @@ interface CreatedOffer {
|
|
|
3003
3422
|
* falls back to `HOSTED_RENDEZVOUS_URL`. Surfaced so `pair offer` can flag,
|
|
3004
3423
|
* never silently, that the daemon is relaying through our infrastructure. */
|
|
3005
3424
|
rendezvousIsHostedDefault: boolean;
|
|
3425
|
+
/** Echoes `CreateOfferInput.scope`. `"host"` when minted with `--host`;
|
|
3426
|
+
* absent for a plain offer. */
|
|
3427
|
+
scope?: "host";
|
|
3006
3428
|
}
|
|
3007
3429
|
interface CreateOfferInput {
|
|
3008
3430
|
/** Time-to-live in ms. Default 10 minutes. */
|
|
3009
3431
|
ttlMs?: number;
|
|
3010
3432
|
/** Rendezvous URL override; falls back to the configured default. */
|
|
3011
3433
|
rendezvousUrl?: string;
|
|
3434
|
+
/** Mint a HOST-scoped offer (PR-C): the accepting side may register this
|
|
3435
|
+
* daemon as a driveable host (`devices add`), not just remote-control it.
|
|
3436
|
+
* Recorded on the resulting `OfferEntry`/`PairingRecord` server-side — this
|
|
3437
|
+
* is the authoritative record of what was granted, independent of the
|
|
3438
|
+
* `scope` param an accepting client's offer URL happens to carry. */
|
|
3439
|
+
scope?: "host";
|
|
3012
3440
|
}
|
|
3013
3441
|
interface PairingRegistryDeps {
|
|
3014
3442
|
/** Lazily load (create on first use) the daemon identity. Kept lazy so a
|
|
@@ -3052,17 +3480,362 @@ interface PairingRegistry {
|
|
|
3052
3480
|
createOffer(input?: CreateOfferInput): Promise<CreatedOffer>;
|
|
3053
3481
|
/** All persisted pairings (copies). Loads `pairings.json` on first call. */
|
|
3054
3482
|
list(): Promise<PairingRecord[]>;
|
|
3483
|
+
/** Rename a pairing by fingerprint or name (cosmetic only — no effect on
|
|
3484
|
+
* auth or routing). Returns false when nothing matched; throws on an
|
|
3485
|
+
* empty `newName`. */
|
|
3486
|
+
rename(idOrName: string, newName: string): Promise<boolean>;
|
|
3055
3487
|
/** Drop a pairing by fingerprint or name; stops its rendezvous connections.
|
|
3056
3488
|
* Returns false when nothing matched. */
|
|
3057
3489
|
revoke(idOrName: string): Promise<boolean>;
|
|
3490
|
+
/** Is a channel (offer or reconnect) currently served for this fingerprint? */
|
|
3491
|
+
isOnline(fingerprint: string): boolean;
|
|
3058
3492
|
/** Start standing reconnect connections for every persisted pairing. Call
|
|
3059
3493
|
* after the gateway is up (so the injected `serve` can reach it). */
|
|
3060
3494
|
startAutoconnect(): Promise<void>;
|
|
3061
3495
|
/** Tear down every offer + reconnect connection and served channel. */
|
|
3062
3496
|
shutdown(): Promise<void>;
|
|
3063
3497
|
}
|
|
3498
|
+
/**
|
|
3499
|
+
* Read-only snapshot of `pairings.json` for callers that just want the
|
|
3500
|
+
* persisted records — no `dial`/`serve`/`loadIdentity` deps, no rendezvous
|
|
3501
|
+
* connections started (e.g. `agentproto doctor`'s devices check). Mirrors
|
|
3502
|
+
* `ensureLoaded`'s parse + v1-legacy flagging; never throws — a missing or
|
|
3503
|
+
* malformed file just yields `[]`.
|
|
3504
|
+
*/
|
|
3505
|
+
declare function readPairingsSnapshot(path?: string): Promise<PairingRecord[]>;
|
|
3064
3506
|
declare function createPairingRegistry(deps: PairingRegistryDeps): PairingRegistry;
|
|
3065
3507
|
|
|
3508
|
+
/**
|
|
3509
|
+
* Daemon-side HOST registry (DEVICES-PLAN PR-C) — the "client" half of
|
|
3510
|
+
* pair/v2, living on the daemon so it's reachable over both an HTTP route
|
|
3511
|
+
* and an MCP tool, not just CLI-local bookkeeping (`client-pairings.ts` /
|
|
3512
|
+
* `pair-transport.ts` are that CLI-local equivalent today).
|
|
3513
|
+
*
|
|
3514
|
+
* Where `pairing-registry.ts` is what a daemon uses to let OTHER machines
|
|
3515
|
+
* pair with and remote-control IT, `host-registry.ts` is what a daemon uses
|
|
3516
|
+
* to register and drive OTHER daemons — reverse pairing. `add()` accepts a
|
|
3517
|
+
* HOST-scoped offer (`agentproto pair offer --host`, offer-url.ts's
|
|
3518
|
+
* `scope`), runs the pair/v2 client handshake, and persists the resulting
|
|
3519
|
+
* pair root; `forwardHttp()` dials on demand, reconnecting over the current
|
|
3520
|
+
* (then previous) epoch token exactly like `pair-transport.ts`'s
|
|
3521
|
+
* `openPairChannel`, forwards one HTTP request over the resulting tunnel
|
|
3522
|
+
* client, and closes. There is no standing connection here — unlike
|
|
3523
|
+
* `PairingRegistry`, a `HostRegistry` never autoconnects or keeps a
|
|
3524
|
+
* reconnect loop parked at the broker; every reach-out is a fresh dial.
|
|
3525
|
+
* That also means shutdown needs no teardown: there is nothing standing to
|
|
3526
|
+
* tear down.
|
|
3527
|
+
*
|
|
3528
|
+
* ## The scope gate
|
|
3529
|
+
*
|
|
3530
|
+
* `add()` REFUSES (throws) any offer whose parsed `scope !== "host"` before
|
|
3531
|
+
* ever dialing anything. This is the default-deny gate PR-C exists to add:
|
|
3532
|
+
* an ordinary "remote-control me" offer (today's `pair offer`, no `--host`)
|
|
3533
|
+
* must not silently become a host registration, even though the underlying
|
|
3534
|
+
* wire capability is identical either way (see offer-url.ts's "Offer scope"
|
|
3535
|
+
* and pairing-registry.ts's `OfferEntry.scope` doc comment for why the
|
|
3536
|
+
* offering daemon's OWN server-side record — not the URL's plaintext
|
|
3537
|
+
* `scope` param — is what's authoritative about what it granted). There is
|
|
3538
|
+
* deliberately no override flag.
|
|
3539
|
+
*
|
|
3540
|
+
* Persisted to `hosts.json` (`~/.agentproto/hosts.json` by default) with the
|
|
3541
|
+
* same atomic-write discipline as `pairings.json` (mkdir, write to a
|
|
3542
|
+
* pid-keyed tmp file at 0600, chmod, rename) and the same lazy-load-on-
|
|
3543
|
+
* first-use pattern.
|
|
3544
|
+
*
|
|
3545
|
+
* ## Online tracking
|
|
3546
|
+
*
|
|
3547
|
+
* `isOnline(fingerprint)` mirrors `PairingRegistry.isOnline`: it's true only
|
|
3548
|
+
* while a `forwardHttp()` call for that fingerprint is in flight (a
|
|
3549
|
+
* reference count, incremented on dial and decremented in a `finally`).
|
|
3550
|
+
* There is no live probe backing `list()` — that would make `devices list`
|
|
3551
|
+
* slow/flaky over N hosts — so `list()`'s `online` is only ever true
|
|
3552
|
+
* immediately after (or during) an actual `forwardHttp`/`devices status`
|
|
3553
|
+
* call, never a background heartbeat. That's enough to make `devices list`
|
|
3554
|
+
* show the right thing right after `devices status`/`exec`, which is what
|
|
3555
|
+
* matters for this PR; a standing/autoconnect host connection with a real
|
|
3556
|
+
* liveness signal is PR-D's to build.
|
|
3557
|
+
*/
|
|
3558
|
+
|
|
3559
|
+
/** `hosts.json` format. */
|
|
3560
|
+
declare const HOSTS_VERSION: 1;
|
|
3561
|
+
/** One persisted host — a daemon this daemon can drive. */
|
|
3562
|
+
interface HostRecord {
|
|
3563
|
+
/** The target daemon's identity fingerprint (32 hex). */
|
|
3564
|
+
fingerprint: string;
|
|
3565
|
+
/** Human-facing label; defaults to the fingerprint. */
|
|
3566
|
+
name: string;
|
|
3567
|
+
/** Target daemon's static X25519 public key, standard base64 SPKI DER —
|
|
3568
|
+
* pinned at `add()` time. */
|
|
3569
|
+
daemonX25519Pub: string;
|
|
3570
|
+
/** Target daemon's static Ed25519 public key, standard base64 SPKI DER —
|
|
3571
|
+
* pinned at `add()` time. */
|
|
3572
|
+
daemonEd25519Pub: string;
|
|
3573
|
+
/** Rendezvous endpoint to reach the host through. */
|
|
3574
|
+
rendezvousUrl: string;
|
|
3575
|
+
/** Long-term shared secret (base64), from which epoch routing tokens
|
|
3576
|
+
* derive — same derivation as `PairingRecord.pairRoot`. */
|
|
3577
|
+
pairRoot: string;
|
|
3578
|
+
/** ISO-8601 first-add timestamp. */
|
|
3579
|
+
createdAt: string;
|
|
3580
|
+
/** ISO-8601 of the most recent successful `forwardHttp`. */
|
|
3581
|
+
lastSeen: string;
|
|
3582
|
+
/** Set on a host added under the retired pair/v1 protocol. Present for
|
|
3583
|
+
* parity with `PairingRecord`/`ClientPairing`; pair/v1 offers are refused
|
|
3584
|
+
* by `parseOfferUrl` before `add()` ever sees them, so this is currently
|
|
3585
|
+
* unreachable — kept so a future downgrade path has somewhere to flag it. */
|
|
3586
|
+
legacy?: true;
|
|
3587
|
+
/** Self-reported by the host at add-time (SANDBOX-VISIBILITY-JOIN) — e.g. a
|
|
3588
|
+
* sandbox provider slug, a provider-side sandbox id, and free-form labels
|
|
3589
|
+
* (PR number, run URL, …). Never verified beyond "the host said so"; purely
|
|
3590
|
+
* descriptive for `device_list`/`devices sessions`. */
|
|
3591
|
+
provider?: string;
|
|
3592
|
+
sandboxId?: string;
|
|
3593
|
+
labels?: Record<string, string>;
|
|
3594
|
+
}
|
|
3595
|
+
/** Optional self-reported metadata `add()` attaches to the resulting
|
|
3596
|
+
* `HostRecord` (see `HostRecord.provider`/`sandboxId`/`labels`). */
|
|
3597
|
+
interface HostJoinMeta {
|
|
3598
|
+
provider?: string;
|
|
3599
|
+
sandboxId?: string;
|
|
3600
|
+
labels?: Record<string, string>;
|
|
3601
|
+
}
|
|
3602
|
+
interface HostRegistryDeps {
|
|
3603
|
+
/** Dial a rendezvous WS and adapt it to a `FrameSink`. Same shape as
|
|
3604
|
+
* `PairingRegistryDeps.dial` — the CLI's `daemonDialRendezvous` satisfies
|
|
3605
|
+
* it directly. Rejects if the dial fails or `signal` aborts. */
|
|
3606
|
+
dial: (wsUrl: string, signal: AbortSignal) => Promise<FrameSink>;
|
|
3607
|
+
/** Path to `hosts.json`. Defaults to `~/.agentproto/hosts.json`. */
|
|
3608
|
+
hostsPath?: string;
|
|
3609
|
+
/** Injectable clock (ms). Defaults to Date.now. */
|
|
3610
|
+
now?: () => number;
|
|
3611
|
+
/** Diagnostic log sink. */
|
|
3612
|
+
log?: (line: string) => void;
|
|
3613
|
+
/** Rendezvous dial ceiling per attempt. Default 15s. */
|
|
3614
|
+
dialTimeoutMs?: number;
|
|
3615
|
+
/** Handshake ceiling per attempt. Default 15s. */
|
|
3616
|
+
handshakeTimeoutMs?: number;
|
|
3617
|
+
}
|
|
3618
|
+
interface ForwardHttpRequest {
|
|
3619
|
+
method: string;
|
|
3620
|
+
path: string;
|
|
3621
|
+
headers?: Record<string, string>;
|
|
3622
|
+
body?: Uint8Array;
|
|
3623
|
+
}
|
|
3624
|
+
interface ForwardHttpResponse {
|
|
3625
|
+
status: number;
|
|
3626
|
+
headers: Record<string, string>;
|
|
3627
|
+
body: Uint8Array;
|
|
3628
|
+
}
|
|
3629
|
+
/** Streaming counterpart of {@link ForwardHttpResponse} — `body` is a Web
|
|
3630
|
+
* `ReadableStream` the caller drains directly (SSE chat/completions, most
|
|
3631
|
+
* notably) instead of a fully-buffered `Uint8Array`. See
|
|
3632
|
+
* {@link HostRegistry.forwardHttpStream}. */
|
|
3633
|
+
interface ForwardHttpStreamResponse {
|
|
3634
|
+
status: number;
|
|
3635
|
+
headers: Record<string, string>;
|
|
3636
|
+
body: ReadableStream<Uint8Array>;
|
|
3637
|
+
}
|
|
3638
|
+
interface HostRegistry {
|
|
3639
|
+
/**
|
|
3640
|
+
* Accept an offer URL and register the target daemon as a driveable host.
|
|
3641
|
+
* REFUSES (throws a plain `Error`) unless the parsed offer has
|
|
3642
|
+
* `scope === "host"` — no dial is attempted in that case. Otherwise: dials
|
|
3643
|
+
* the offer's rendezvous, runs the pair/v2 client handshake, verifies the
|
|
3644
|
+
* derived peer fingerprint matches the offer's `id` (defence in depth,
|
|
3645
|
+
* mirrors `pair-transport.ts`'s `acceptOffer`), derives the pair root, and
|
|
3646
|
+
* persists a `HostRecord` (upserted by fingerprint — re-adding replaces).
|
|
3647
|
+
*/
|
|
3648
|
+
add(offerUrl: string, name?: string, meta?: HostJoinMeta): Promise<{
|
|
3649
|
+
fingerprint: string;
|
|
3650
|
+
name: string;
|
|
3651
|
+
rendezvousUrl: string;
|
|
3652
|
+
}>;
|
|
3653
|
+
/** All persisted hosts (copies). Loads `hosts.json` on first call. */
|
|
3654
|
+
list(): Promise<HostRecord[]>;
|
|
3655
|
+
/** Rename a host (fingerprint or current name). Returns false when nothing
|
|
3656
|
+
* matched; throws on an empty `newName`. */
|
|
3657
|
+
rename(idOrName: string, newName: string): Promise<boolean>;
|
|
3658
|
+
/** Drop a host by fingerprint or name. Returns false when nothing matched. */
|
|
3659
|
+
revoke(idOrName: string): Promise<boolean>;
|
|
3660
|
+
/** Is a `forwardHttp` call for this fingerprint in flight right now? */
|
|
3661
|
+
isOnline(fingerprint: string): boolean;
|
|
3662
|
+
/**
|
|
3663
|
+
* On-demand: dial the host, run the client handshake against the current
|
|
3664
|
+
* epoch's tokens (falling back to the previous epoch, bridging clock
|
|
3665
|
+
* skew — same two-attempt shape as `pair-transport.ts`'s
|
|
3666
|
+
* `openPairChannel`), forward one HTTP request over the resulting tunnel
|
|
3667
|
+
* client, then close. Throws with a re-pair hint if every attempt hangs up
|
|
3668
|
+
* on the hello (likely a pair/v1 peer).
|
|
3669
|
+
*/
|
|
3670
|
+
forwardHttp(idOrName: string, req: ForwardHttpRequest): Promise<ForwardHttpResponse>;
|
|
3671
|
+
/**
|
|
3672
|
+
* Same dial/handshake/retry shape as {@link forwardHttp}, but for a
|
|
3673
|
+
* streamed response (SSE, NDJSON, long-poll) — the daemon-inference proxy's
|
|
3674
|
+
* `/v1/chat/completions` most notably, which must relay tokens as they
|
|
3675
|
+
* arrive rather than buffering the whole completion first. Unlike
|
|
3676
|
+
* `forwardHttp`, the tunnel client is kept open (and `isOnline` stays true)
|
|
3677
|
+
* until the returned `body` stream is fully drained, errors, or is
|
|
3678
|
+
* cancelled — only then does it close and the online count drop.
|
|
3679
|
+
*/
|
|
3680
|
+
forwardHttpStream(idOrName: string, req: ForwardHttpRequest): Promise<ForwardHttpStreamResponse>;
|
|
3681
|
+
}
|
|
3682
|
+
declare function createHostRegistry(deps: HostRegistryDeps): HostRegistry;
|
|
3683
|
+
|
|
3684
|
+
/**
|
|
3685
|
+
* Daemon-side JOIN TOKEN registry (SANDBOX-VISIBILITY-JOIN, stacked on
|
|
3686
|
+
* DEVICES-PLAN PR-C's `host-registry.ts`).
|
|
3687
|
+
*
|
|
3688
|
+
* `host-registry.ts`'s `add()` requires a HUMAN to relay a host-scoped offer
|
|
3689
|
+
* URL from the box to this daemon (`agentproto pair offer --host` on the box,
|
|
3690
|
+
* `agentproto devices add <url>` here) — fine for a laptop, impossible for an
|
|
3691
|
+
* unattended CI sandbox with no one watching its logs to copy a one-time,
|
|
3692
|
+
* 10-minute-TTL secret. A join token flips who initiates: this daemon mints a
|
|
3693
|
+
* long-lived, revocable, reusable credential ahead of time (stored as a
|
|
3694
|
+
* GitHub secret, say); a box started with `AGENTPROTO_JOIN=<token>` dials
|
|
3695
|
+
* in — over the SAME rendezvous/pair-v2 primitives, no protocol change — and
|
|
3696
|
+
* this daemon adds it as a host automatically.
|
|
3697
|
+
*
|
|
3698
|
+
* ## Why this isn't just a long-lived `host-registry.ts` offer
|
|
3699
|
+
*
|
|
3700
|
+
* pair/v2 deliberately gives a CLIENT no persistent identity key (see
|
|
3701
|
+
* `pairing-registry.ts`'s "Reconnect authentication" doc) — only the
|
|
3702
|
+
* DAEMON/offerer side has stable keys a peer can pin and re-dial later.
|
|
3703
|
+
* `HostRegistry.forwardHttp` needs exactly those stable keys to reach a host
|
|
3704
|
+
* on demand, so the box MUST end up as the offerer of its own host-scoped
|
|
3705
|
+
* offer (a normal, single-use `pair offer --host`) with THIS daemon
|
|
3706
|
+
* `add()`-ing it — `host-registry.ts` is not touched by this module at all.
|
|
3707
|
+
*
|
|
3708
|
+
* What the join token replaces is only the relay step: this daemon parks a
|
|
3709
|
+
* STANDING accept loop (`side=daemon`, exactly like a `pair offer`'s park,
|
|
3710
|
+
* except never single-use) on a route deterministically derived from the
|
|
3711
|
+
* token's own secret. A box holding the token dials that route as the
|
|
3712
|
+
* CLIENT — the role a browser/CLI plays consuming a normal offer — and its
|
|
3713
|
+
* sealed hello's `clientName` carries a small JSON envelope: the box's own
|
|
3714
|
+
* self-minted host-scoped offer URL (freshly created, in-process, via its
|
|
3715
|
+
* own `PairingRegistry.createOffer({scope:"host"})` — see
|
|
3716
|
+
* `packages/cli/src/commands/serve.ts`'s `AGENTPROTO_JOIN` boot wiring) plus
|
|
3717
|
+
* a self-reported name/provider/sandboxId/labels. On receipt, this daemon
|
|
3718
|
+
* just calls `HostRegistry.add(offerUrl, name, meta)` — the exact call a
|
|
3719
|
+
* human would type — closes the bootstrap channel, and re-parks for the next
|
|
3720
|
+
* box. No new wire messages, no new crypto: two ordinary pair/v2 handshakes
|
|
3721
|
+
* back to back, the first only ever used to carry one string.
|
|
3722
|
+
*
|
|
3723
|
+
* ## Reuse and revocation
|
|
3724
|
+
*
|
|
3725
|
+
* Unlike an `OfferEntry` (`pairing-registry.ts`), a join token's
|
|
3726
|
+
* `{route, auth}` never rotates and is never "spent" — `useCount`/`maxUses`/
|
|
3727
|
+
* `expiresAt`/`revokedAt` (all checked inside `verifyAuthToken`, which may be
|
|
3728
|
+
* async) gate reuse instead. This is the same trust shape as an existing
|
|
3729
|
+
* epoch reconnect token (valid, unrotated, for its whole day) stretched over
|
|
3730
|
+
* a longer, explicit, revocable window — not a new security primitive.
|
|
3731
|
+
*
|
|
3732
|
+
* ## A known limitation
|
|
3733
|
+
*
|
|
3734
|
+
* One token parks on exactly one fixed route, so exactly one box can be
|
|
3735
|
+
* mid-handshake on it at a time; a second box dialing in the same instant
|
|
3736
|
+
* either waits for the loop to re-park (typical: the ceremony is a single
|
|
3737
|
+
* short-TTL round trip) or, in the worst case, times out and retries with
|
|
3738
|
+
* backoff. Fine for the sequential-CI-review case this exists for; a fleet
|
|
3739
|
+
* of concurrently-joining boxes would want multiple parked slots per token,
|
|
3740
|
+
* deferred (see PR body).
|
|
3741
|
+
*
|
|
3742
|
+
* Persisted to `~/.agentproto/join-tokens.json`, same atomic-write + lazy-
|
|
3743
|
+
* load discipline as `hosts.json`/`pairings.json`. The raw `secret` — not a
|
|
3744
|
+
* hash — is stored, exactly like `pairings.json` stores `pairRoot` in the
|
|
3745
|
+
* clear: it is crypto material this daemon must re-derive `{route, auth}`
|
|
3746
|
+
* from on every boot, not a password to be hashed.
|
|
3747
|
+
*/
|
|
3748
|
+
|
|
3749
|
+
/** `join-tokens.json` format. */
|
|
3750
|
+
declare const JOIN_TOKENS_VERSION: 1;
|
|
3751
|
+
/** One persisted join token. `secret` is the crypto material a box's
|
|
3752
|
+
* `AGENTPROTO_JOIN` value carries too — never log or echo it once minted. */
|
|
3753
|
+
interface JoinTokenRecord {
|
|
3754
|
+
/** Short random id (CLI/MCP target), distinct from `secret`. */
|
|
3755
|
+
id: string;
|
|
3756
|
+
/** Human-facing label (e.g. "ci-reviewer"). */
|
|
3757
|
+
name: string;
|
|
3758
|
+
/** One-time-offer-shaped secret (base64url) — re-derives `{route, auth}`
|
|
3759
|
+
* via `deriveOfferTokens`. Never rotates for this token's lifetime. */
|
|
3760
|
+
secret: string;
|
|
3761
|
+
/** Rendezvous endpoint boxes dial through. */
|
|
3762
|
+
rendezvousUrl: string;
|
|
3763
|
+
/** ISO-8601 mint time. */
|
|
3764
|
+
createdAt: string;
|
|
3765
|
+
/** ISO-8601 expiry — always set (`create()` defaults `ttlMs`). */
|
|
3766
|
+
expiresAt: string;
|
|
3767
|
+
/** Reuse ceiling. Absent = unlimited (until `expiresAt`/revoke). */
|
|
3768
|
+
maxUses?: number;
|
|
3769
|
+
/** Successful joins so far. */
|
|
3770
|
+
useCount: number;
|
|
3771
|
+
/** ISO-8601 of the most recent successful join. */
|
|
3772
|
+
lastUsedAt?: string;
|
|
3773
|
+
/** ISO-8601 revoke time. Present ⇒ inactive; kept for `list()` history. */
|
|
3774
|
+
revokedAt?: string;
|
|
3775
|
+
}
|
|
3776
|
+
interface CreateJoinTokenInput {
|
|
3777
|
+
name: string;
|
|
3778
|
+
/** Time-to-live in ms. Default 90 days. */
|
|
3779
|
+
ttlMs?: number;
|
|
3780
|
+
/** Reuse ceiling. Absent = unlimited. */
|
|
3781
|
+
maxUses?: number;
|
|
3782
|
+
/** Rendezvous URL override; falls back to the configured default. */
|
|
3783
|
+
rendezvousUrl?: string;
|
|
3784
|
+
}
|
|
3785
|
+
interface CreatedJoinToken {
|
|
3786
|
+
id: string;
|
|
3787
|
+
name: string;
|
|
3788
|
+
/** The `AGENTPROTO_JOIN` value — an `agentproto://pair?…&scope=host` URL.
|
|
3789
|
+
* Shown ONCE; `list()` never returns it. */
|
|
3790
|
+
token: string;
|
|
3791
|
+
rendezvousUrl: string;
|
|
3792
|
+
expiresAt: string;
|
|
3793
|
+
}
|
|
3794
|
+
interface JoinTokenRegistryDeps {
|
|
3795
|
+
/** Lazily load (create on first use) the daemon identity — same contract
|
|
3796
|
+
* as `PairingRegistryDeps.loadIdentity`. */
|
|
3797
|
+
loadIdentity: () => Promise<DaemonIdentity>;
|
|
3798
|
+
/** Path to `join-tokens.json`. Defaults to `~/.agentproto/join-tokens.json`. */
|
|
3799
|
+
joinTokensPath?: string;
|
|
3800
|
+
/** Configured rendezvous URL default — same three-state contract as
|
|
3801
|
+
* `PairingRegistryDeps.defaultRendezvousUrl` (absent → hosted default;
|
|
3802
|
+
* `""` → explicit opt-out, fails closed without `--rendezvous`). */
|
|
3803
|
+
defaultRendezvousUrl?: string;
|
|
3804
|
+
/** Dial a rendezvous WS and adapt it to a `FrameSink`. Same shape as
|
|
3805
|
+
* `PairingRegistryDeps.dial`/`HostRegistryDeps.dial`. */
|
|
3806
|
+
dial: (wsUrl: string, signal: AbortSignal) => Promise<FrameSink>;
|
|
3807
|
+
/** Called once per successful join, with the box's self-minted host offer
|
|
3808
|
+
* URL and self-reported name/meta — normally `hostRegistry.add`. Errors
|
|
3809
|
+
* are logged and swallowed: a bad/unreachable offer from one join must not
|
|
3810
|
+
* crash the standing accept loop. */
|
|
3811
|
+
addHost: (offerUrl: string, name?: string, meta?: HostJoinMeta) => Promise<unknown>;
|
|
3812
|
+
/** Injectable clock (ms). Defaults to Date.now. */
|
|
3813
|
+
now?: () => number;
|
|
3814
|
+
/** Diagnostic log sink. */
|
|
3815
|
+
log?: (line: string) => void;
|
|
3816
|
+
/** Handshake ceiling per attempt. Default 110s (mirrors PairingRegistry's
|
|
3817
|
+
* park timeout, kept just under the broker's own). */
|
|
3818
|
+
handshakeTimeoutMs?: number;
|
|
3819
|
+
/** Reconnect backoff floor / ceiling (ms) after a failed dial. Defaults 1s/30s. */
|
|
3820
|
+
reconnectMinMs?: number;
|
|
3821
|
+
reconnectMaxMs?: number;
|
|
3822
|
+
}
|
|
3823
|
+
interface JoinTokenRegistry {
|
|
3824
|
+
/** Mint a token and start its standing accept loop. */
|
|
3825
|
+
create(input: CreateJoinTokenInput): Promise<CreatedJoinToken>;
|
|
3826
|
+
/** All persisted tokens (copies), `secret` stripped. Loads on first call. */
|
|
3827
|
+
list(): Promise<Omit<JoinTokenRecord, "secret">[]>;
|
|
3828
|
+
/** Revoke by id or name — stops its accept loop. Returns false if nothing
|
|
3829
|
+
* matched or it was already revoked. */
|
|
3830
|
+
revoke(idOrName: string): Promise<boolean>;
|
|
3831
|
+
/** Start standing accept loops for every active persisted token. Call once
|
|
3832
|
+
* at boot, after the gateway/hostRegistry are wired. */
|
|
3833
|
+
startAutoconnect(): Promise<void>;
|
|
3834
|
+
/** Tear down every accept loop. */
|
|
3835
|
+
shutdown(): Promise<void>;
|
|
3836
|
+
}
|
|
3837
|
+
declare function createJoinTokenRegistry(deps: JoinTokenRegistryDeps): JoinTokenRegistry;
|
|
3838
|
+
|
|
3066
3839
|
/**
|
|
3067
3840
|
* Shared types for pluggable sandbox providers.
|
|
3068
3841
|
*
|
|
@@ -3873,6 +4646,29 @@ declare function createActivityProjector(opts: {
|
|
|
3873
4646
|
resolvePrState?: PrStateResolver;
|
|
3874
4647
|
}): ActivityProjector;
|
|
3875
4648
|
|
|
4649
|
+
/**
|
|
4650
|
+
* `~/.agentproto/bundles.json` — named sets of imported MCPs + skills (+
|
|
4651
|
+
* optionally the daemon's own `/mcp`), attachable to any harness in one
|
|
4652
|
+
* `agent_start({bundles:["research"]})` call.
|
|
4653
|
+
*
|
|
4654
|
+
* Same load/save/atomic-write pattern as `user-presets.ts`; validation
|
|
4655
|
+
* against the live imported-MCP set is the single boundary every writer
|
|
4656
|
+
* (MCP tools, HTTP routes) goes through (`saveBundle`).
|
|
4657
|
+
*/
|
|
4658
|
+
|
|
4659
|
+
declare const bundlesFileSchema: z.ZodObject<{
|
|
4660
|
+
version: z.ZodLiteral<1>;
|
|
4661
|
+
bundles: z.ZodArray<z.ZodObject<{
|
|
4662
|
+
id: z.ZodString;
|
|
4663
|
+
label: z.ZodString;
|
|
4664
|
+
description: z.ZodOptional<z.ZodString>;
|
|
4665
|
+
mcpImports: z.ZodArray<z.ZodString>;
|
|
4666
|
+
includeDaemon: z.ZodOptional<z.ZodBoolean>;
|
|
4667
|
+
skills: z.ZodArray<z.ZodString>;
|
|
4668
|
+
}, z.core.$strip>>;
|
|
4669
|
+
}, z.core.$strip>;
|
|
4670
|
+
type BundlesFile = z.infer<typeof bundlesFileSchema>;
|
|
4671
|
+
|
|
3876
4672
|
/**
|
|
3877
4673
|
* Spawn-time role profiles — the primitive that decides whether a
|
|
3878
4674
|
* spawned agent may itself delegate (spawn further children).
|
|
@@ -4113,6 +4909,21 @@ interface WorktreeProvisionRequest {
|
|
|
4113
4909
|
/** Free-text label (the session's `label`) the provisioner may fold into a
|
|
4114
4910
|
* minted slug for human readability. Never load-bearing for correctness. */
|
|
4115
4911
|
labelHint?: string;
|
|
4912
|
+
/** Absolute path the provisioner should append the setup hooks' FULL
|
|
4913
|
+
* stdout+stderr to (see `worktree.provision`'s `setupLogPath` input and
|
|
4914
|
+
* `@agentproto/worktree`'s `runSetup`). `session-spawn.ts` passes the
|
|
4915
|
+
* spawning session's own transcript dir here, so a failing setup hook's
|
|
4916
|
+
* full output survives even after a subsequently-reclaimed worktree is
|
|
4917
|
+
* gone — the short `HookError` message alone only carries a filtered
|
|
4918
|
+
* tail. Omitted callers (e.g. `agentproto worktree new`, no session to
|
|
4919
|
+
* log into) simply get no persisted log. */
|
|
4920
|
+
setupLogPath?: string;
|
|
4921
|
+
/** Re-run a failing `worktree.setup` command exactly once before giving
|
|
4922
|
+
* up — see `worktree.provision`'s `retrySetupOnFailure` input and
|
|
4923
|
+
* `runSetup`'s doc for why this is a bounded complement, not a
|
|
4924
|
+
* substitute, and why the daemon opts in while `agentproto worktree new`
|
|
4925
|
+
* doesn't. */
|
|
4926
|
+
retrySetupOnFailure?: boolean;
|
|
4116
4927
|
}
|
|
4117
4928
|
/** The provisioner's outcome. `isolated: false` is NOT a failure — it means
|
|
4118
4929
|
* there was nothing to isolate (`cwd` sits in no git repo), and the caller
|
|
@@ -4329,6 +5140,15 @@ interface SpawnAgentSessionDeps {
|
|
|
4329
5140
|
* `loadConfig` when omitted; tests inject a stub to avoid touching
|
|
4330
5141
|
* the real file. */
|
|
4331
5142
|
loadDefaultsConfig?: () => Promise<SpawnDefaultsConfig | undefined>;
|
|
5143
|
+
/** Loads `~/.agentproto/bundles.json` for capability-bundle expansion
|
|
5144
|
+
* (`agent_start.bundles` / config `defaults.bundles`, PLAN D phase 1).
|
|
5145
|
+
* Defaults to the real loader when omitted; tests inject a stub to avoid
|
|
5146
|
+
* touching the real file. */
|
|
5147
|
+
loadBundlesConfig?: () => Promise<BundlesFile>;
|
|
5148
|
+
/** Loads `~/.agentproto/imported-mcps.json` — resolves a bundle's
|
|
5149
|
+
* `mcpImports` ids to their alias/snapshot and detects dangling ones.
|
|
5150
|
+
* Defaults to the real loader when omitted; tests inject a stub. */
|
|
5151
|
+
loadImportedMcpsConfig?: () => Promise<ImportedMcpsConfig>;
|
|
4332
5152
|
/** Builds the per-session headless-browser mount for `browser:
|
|
4333
5153
|
* "headless"` (see `browser-mount.ts`). Defaults to the real resolver
|
|
4334
5154
|
* (installs chrome-devtools-mcp on first use); tests inject a stub. */
|
|
@@ -4394,6 +5214,18 @@ interface SpawnAgentSessionDeps {
|
|
|
4394
5214
|
* manifest not covering the requested model still reaches the driver,
|
|
4395
5215
|
* same as before this guard existed. */
|
|
4396
5216
|
listCatalogModels?: CatalogModelsLister;
|
|
5217
|
+
/** Ensure the local `llm-endpoint` proxy sidecar (`LlmEndpointRegistry`)
|
|
5218
|
+
* is up before a spawn that bills through it. Without this hook, a spawn
|
|
5219
|
+
* whose resolved gateway is `"llm-endpoint"` assumed the sidecar was
|
|
5220
|
+
* already running (started externally, or via the `llm_endpoint_start`
|
|
5221
|
+
* MCP tool) — a fresh install with `features.llmEndpoint` on but nothing
|
|
5222
|
+
* having called that tool yet would spawn fine and only fail once the
|
|
5223
|
+
* adapter made its first request. Wired to the registry's own
|
|
5224
|
+
* (idempotent) `start()` at the composition root; omitted ⇒ unchanged
|
|
5225
|
+
* pre-existing behavior. Best-effort: a rejection here is swallowed by
|
|
5226
|
+
* the caller, not surfaced as a spawn failure — the adapter's own first
|
|
5227
|
+
* request is left to report the real connection error. */
|
|
5228
|
+
ensureLlmEndpointRunning?: () => Promise<void>;
|
|
4397
5229
|
}
|
|
4398
5230
|
interface SpawnAgentSessionInput {
|
|
4399
5231
|
adapter: string;
|
|
@@ -4455,7 +5287,19 @@ interface SpawnAgentSessionInput {
|
|
|
4455
5287
|
* blank. Not exposed on the MCP `agent_start` tool today — only the
|
|
4456
5288
|
* HTTP route (`sessions restart`) passes this. */
|
|
4457
5289
|
resumeSessionId?: string;
|
|
4458
|
-
|
|
5290
|
+
/** The caller's initial ask — a plain string (the common case, composed
|
|
5291
|
+
* with the role/AGENTS.md/RULES.md preamble below into one string turn),
|
|
5292
|
+
* or a content block / block array (an attachment-bearing first message,
|
|
5293
|
+
* same loose shape `POST /sessions/:id/prompt` accepts). The block form
|
|
5294
|
+
* is a narrow path: composing daemon-synthesized preamble TEXT onto a
|
|
5295
|
+
* turn that opens with an image isn't meaningful string concatenation,
|
|
5296
|
+
* so it SKIPS the whole preamble composition below (role disposition,
|
|
5297
|
+
* AGENTS.md, RULES.md, parent-lineage line, posture-as-prompt fallback)
|
|
5298
|
+
* and is sent to the adapter verbatim — see `effectivePrompt`'s doc.
|
|
5299
|
+
* Acceptable because this path is human/UI-initiated (an attachment
|
|
5300
|
+
* typed into a chat composer), not the orchestrator-driven role spawns
|
|
5301
|
+
* that actually depend on the composed disposition text. */
|
|
5302
|
+
prompt?: string | Record<string, unknown> | unknown[];
|
|
4459
5303
|
label?: string;
|
|
4460
5304
|
/** Explicit session title (SPEC-3 FIX C, `agentproto sessions start
|
|
4461
5305
|
* --title`). When set, it wins over the first-sentence derivation from
|
|
@@ -4477,6 +5321,22 @@ interface SpawnAgentSessionInput {
|
|
|
4477
5321
|
* such option (e.g. claude-code, which auto-discovers skills) ignore
|
|
4478
5322
|
* it. See `resolveSpawnDefaults` / `normalizeSkillsOption`. */
|
|
4479
5323
|
skills?: string[];
|
|
5324
|
+
/** Capability bundle ids (`bundle_list`) to attach (PLAN D phase 1). Merged
|
|
5325
|
+
* with config.json's `defaults.bundles` / `defaults.adapters.<slug>.bundles`
|
|
5326
|
+
* with the same precedence/replace semantics as `skills` — see
|
|
5327
|
+
* `resolveBundleDefaults`. Each bundle expands to its `mcpImports` (mounted
|
|
5328
|
+
* as native MCP servers via the daemon's `/mcp/imported/<id>` passthrough,
|
|
5329
|
+
* never the two-step `mcp_imported_*` indirection), optionally the
|
|
5330
|
+
* daemon's own `/mcp` (`includeDaemon`), and its `skills` (unioned into
|
|
5331
|
+
* the resolved skill list). Ignored (with a warning) for a sandbox spawn —
|
|
5332
|
+
* the box cannot reach this daemon's loopback `/mcp`. */
|
|
5333
|
+
bundles?: string[];
|
|
5334
|
+
/** Explicitly mount the daemon's own scoped `/mcp` for this spawn — the
|
|
5335
|
+
* only way an adapter outside `shouldInjectDaemonSelfMount`'s allowlist
|
|
5336
|
+
* (opencode, codex, gemini, …) gets it. Merged with config's
|
|
5337
|
+
* `defaults.adapters.<slug>.daemonMount` (per-spawn wins). Ignored (with a
|
|
5338
|
+
* warning) for a sandbox spawn. */
|
|
5339
|
+
daemonMount?: boolean;
|
|
4480
5340
|
model?: string;
|
|
4481
5341
|
effort?: string;
|
|
4482
5342
|
/** Decomposed route identity. This is canonical transport; `mode` remains
|
|
@@ -4615,6 +5475,12 @@ interface SpawnAgentSessionInput {
|
|
|
4615
5475
|
* external webhook path (`notifyUrl`) already fires regardless of this
|
|
4616
5476
|
* flag. */
|
|
4617
5477
|
notifyParentOnCrash?: boolean;
|
|
5478
|
+
/** Per-spawn opt-out of sentinel auto-link (AIP-60 §6/step 4): `false`
|
|
5479
|
+
* means a PR this session opens is never auto-watched, regardless of
|
|
5480
|
+
* `config.sentinel.autoWatchPrs`. Recorded verbatim onto
|
|
5481
|
+
* {@link SessionDescriptor.sentinelAutoWatch}; `undefined`/`true` = allowed
|
|
5482
|
+
* (still gated by the config default). */
|
|
5483
|
+
sentinel?: boolean;
|
|
4618
5484
|
/** Exempt this session from the idle-reaper (`isReapable` in
|
|
4619
5485
|
* idle-reaper.ts) regardless of how long it sits idle. Stamped straight
|
|
4620
5486
|
* onto the descriptor — see `SessionDescriptor.keepAlive`. Default false
|
|
@@ -4955,6 +5821,8 @@ interface BranchGcPlanView {
|
|
|
4955
5821
|
includeReviewed: boolean;
|
|
4956
5822
|
generatedAt: string;
|
|
4957
5823
|
otherRemoteRefs: number;
|
|
5824
|
+
/** true = base-remote `fetch --prune` succeeded; false = failed; null = skipped / no remote. */
|
|
5825
|
+
fetched: boolean | null;
|
|
4958
5826
|
entries: BranchGcPlanEntryView[];
|
|
4959
5827
|
}
|
|
4960
5828
|
/** kind → class → count, and kind → (status | "protected") → count. */
|
|
@@ -5033,6 +5901,22 @@ interface BranchGcVerdictLookupInput {
|
|
|
5033
5901
|
* lookup, so a caller can check one tip without re-running a whole `branch_gc` plan. */
|
|
5034
5902
|
type BranchGcVerdictReader = (input: BranchGcVerdictLookupInput) => Promise<BranchGcVerdictRecordView | null>;
|
|
5035
5903
|
|
|
5904
|
+
/**
|
|
5905
|
+
* Set by the tunnel server (never a caller) on every `http_request` it
|
|
5906
|
+
* forwards from a HOST-scoped pairing (DEVICES-PLAN item 1) —
|
|
5907
|
+
* `buildDaemonTunnelServerOptions`'s `pairingScope` in
|
|
5908
|
+
* `packages/cli/src/util/tunnel-serve.ts`. `httpInjectHeaders` overrides any
|
|
5909
|
+
* same-named header the ORIGINAL caller's frame carried, so this can never be
|
|
5910
|
+
* spoofed by whoever dials in over a pairing — only the daemon's own
|
|
5911
|
+
* server-side scope decision at serve time ever sets it.
|
|
5912
|
+
*
|
|
5913
|
+
* Deliberately NOT paired with an `isLoopback` bypass the way most other
|
|
5914
|
+
* routes are: every `http_request` a tunnel forwards to THIS server lands as
|
|
5915
|
+
* a loopback socket (the daemon dialing its own gateway via `fetch`), so
|
|
5916
|
+
* `isLoopback` is true for ALL forwarded traffic — host-scoped or not — and
|
|
5917
|
+
* cannot distinguish them. `handleDeviceInference` checks this header alone.
|
|
5918
|
+
*/
|
|
5919
|
+
declare const HOST_SCOPE_HEADER = "x-agentproto-host-scope";
|
|
5036
5920
|
/**
|
|
5037
5921
|
* Pluggable adapter resolver — keeps the runtime package free of any
|
|
5038
5922
|
* @agentproto/cli dep. The host (cli `serve`, playground, embedding
|
|
@@ -5158,12 +6042,10 @@ type AgentAdapterResolver = (slug: string) => Promise<{
|
|
|
5158
6042
|
}): Promise<AgentSessionLike>;
|
|
5159
6043
|
/** Display label for the descriptor's `command` field. */
|
|
5160
6044
|
commandPreview?: string;
|
|
5161
|
-
/** Best-effort per-session usage reader (adapter-specific, e.g. hermes
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
tokensOut?: number;
|
|
5166
|
-
} | null>;
|
|
6045
|
+
/** Best-effort per-session usage reader (adapter-specific, e.g. hermes
|
|
6046
|
+
* state.db, the claude-code transcript JSONL). `ctx` carries the spawn's
|
|
6047
|
+
* cwd + isolated config dir for readers that locate their store by them. */
|
|
6048
|
+
readUsage?: (adapterSessionId: string, ctx?: UsageReadContext) => Promise<UsageReadResult | null>;
|
|
5167
6049
|
/** AIP-45 `options[]` this adapter's manifest declares (id + type only —
|
|
5168
6050
|
* no spawn internals). Lets `session-spawn.ts` fold a config-level
|
|
5169
6051
|
* `defaults.skills` list into `options.skills` using the shape the
|
|
@@ -5313,169 +6195,48 @@ interface AdapterInstallResult {
|
|
|
5313
6195
|
* post-install re-list failed (the install itself may still have
|
|
5314
6196
|
* succeeded — read `ok`). */
|
|
5315
6197
|
status?: "supported" | "available" | "ready" | "unresolvable";
|
|
5316
|
-
/** True when the install failed ONLY because a manifest setup step
|
|
5317
|
-
* declares `interactive: true` and this process has no TTY to run it
|
|
5318
|
-
* in (e.g. openclaw's `onboard --install-daemon` TUI). The remedy is a
|
|
5319
|
-
* real terminal — `agentproto setup <slug>` — which a UI can offer
|
|
5320
|
-
* directly as a PTY terminal session. */
|
|
5321
|
-
needsInteractiveSetup?: boolean;
|
|
5322
|
-
}
|
|
5323
|
-
/**
|
|
5324
|
-
* Installs an agent CLI adapter by slug and reports the outcome. Wired by
|
|
5325
|
-
* the daemon from `@agentproto/cli`'s `installAdapter` (which knows the
|
|
5326
|
-
* catalog + the two install classes); the runtime stays cli-free and only
|
|
5327
|
-
* exposes the verb. Must never throw for an ordinary install failure —
|
|
5328
|
-
* report it via `ok:false` + `message` — so the MCP tool / HTTP route can
|
|
5329
|
-
* return a clean result instead of a 500.
|
|
5330
|
-
*/
|
|
5331
|
-
type AgentAdapterInstaller = (slug: string) => Promise<AdapterInstallResult>;
|
|
5332
|
-
/** Loads the read-only vendor/product/route catalog (SPEC §5) for
|
|
5333
|
-
* `GET /catalog/models` + the `catalog_models` MCP tool. A host wires this
|
|
5334
|
-
* from `buildCatalogModels` (`catalog-models.ts`) fed by its installed
|
|
5335
|
-
* adapters + `@agentproto/auth`'s `listAuthProfiles()` — the query params
|
|
5336
|
-
* are forwarded verbatim from the request. Omitted ⇒ the route/tool
|
|
5337
|
-
* report "not enabled" (same convention as `listAgentAdapters`). */
|
|
5338
|
-
type CatalogModelsLister = (query: CatalogModelsQuery) => Promise<CatalogModelsResponse>;
|
|
5339
|
-
interface AuthOptions {
|
|
5340
|
-
mode: "none" | "bearer";
|
|
5341
|
-
token?: string;
|
|
5342
|
-
}
|
|
5343
|
-
|
|
5344
|
-
/** One background task as mirrored onto `SessionDescriptor.backgroundTasks`. */
|
|
5345
|
-
interface SessionBackgroundTask {
|
|
5346
|
-
taskId: string;
|
|
5347
|
-
/** Friendly task type (`"shell"`, `"monitor"`, ...), when the agent says. */
|
|
5348
|
-
taskKind?: string;
|
|
5349
|
-
description?: string;
|
|
5350
|
-
/** Where the task writes its output, when the agent says. */
|
|
5351
|
-
outputFile?: string;
|
|
5352
|
-
status: "running" | "paused" | "completed" | "failed" | "stopped";
|
|
5353
|
-
summary?: string;
|
|
5354
|
-
toolCallId?: string;
|
|
5355
|
-
/** ISO 8601 — when the daemon first heard of the task. */
|
|
5356
|
-
startedAt: string;
|
|
5357
|
-
}
|
|
5358
|
-
|
|
5359
|
-
/**
|
|
5360
|
-
* Typed inter-session message envelope (AIP-46 §Session messages).
|
|
5361
|
-
*
|
|
5362
|
-
* A PROMPT is an instruction from whoever drives a session; a MESSAGE is a
|
|
5363
|
-
* report between sessions in the same tree whose sender the DAEMON attests.
|
|
5364
|
-
* Everything in `from` is computed here from the caller's verified identity
|
|
5365
|
-
* and the session forest — a send request can never set it.
|
|
5366
|
-
*
|
|
5367
|
-
* Pure module: no registry, no I/O. The registry (sessions.ts) carries a
|
|
5368
|
-
* `SessionMessage` on a queued prompt (`QueuedPrompt.envelope`), renders it
|
|
5369
|
-
* into the model's context with `renderSessionMessages`, and records it as
|
|
5370
|
-
* its own `session-message` transcript record — never a `user-prompt`.
|
|
5371
|
-
*/
|
|
5372
|
-
/** Sender's position in the tree, relative to the recipient. */
|
|
5373
|
-
type MessageRelation = "child" | "parent" | "sibling" | "human" | "system";
|
|
5374
|
-
type MessageKind = "report" | "question" | "blocker" | "done" | "notice";
|
|
5375
|
-
type MessageUrgency = "fyi" | "next-turn" | "steer" | "interrupt";
|
|
5376
|
-
type MessageDeliveryVia = "wait" | "steer" | "turn" | "interrupt" | "inbox";
|
|
5377
|
-
declare const MESSAGE_KINDS: readonly MessageKind[];
|
|
5378
|
-
declare const MESSAGE_URGENCIES: readonly MessageUrgency[];
|
|
5379
|
-
interface SessionMessageFrom {
|
|
5380
|
-
/** Absent only for relation `human` | `system`. */
|
|
5381
|
-
sessionId?: string;
|
|
5382
|
-
/** Registry label at send time — display only, never identity. */
|
|
5383
|
-
label?: string;
|
|
5384
|
-
role?: string;
|
|
5385
|
-
adapter?: string;
|
|
5386
|
-
relation: MessageRelation;
|
|
5387
|
-
}
|
|
5388
|
-
interface SessionMessage {
|
|
5389
|
-
/** `msg_<uuid8>`, daemon-minted. */
|
|
5390
|
-
id: string;
|
|
5391
|
-
ts: string;
|
|
5392
|
-
/** Recipient sessionId. */
|
|
5393
|
-
to: string;
|
|
5394
|
-
from: SessionMessageFrom;
|
|
5395
|
-
kind: MessageKind;
|
|
5396
|
-
/** Requested urgency. */
|
|
5397
|
-
urgency: MessageUrgency;
|
|
5398
|
-
/** Stamped on delivery. */
|
|
5399
|
-
delivered?: {
|
|
5400
|
-
via: MessageDeliveryVia;
|
|
5401
|
-
at: string;
|
|
5402
|
-
turnSeq?: number;
|
|
5403
|
-
};
|
|
5404
|
-
/** Thread id; defaults to the first message's id. */
|
|
5405
|
-
correlationId?: string;
|
|
5406
|
-
/** A `SessionMessage.id` this answers. */
|
|
5407
|
-
replyTo?: string;
|
|
5408
|
-
text: string;
|
|
5409
|
-
/** Optional structured payload (≤ `MAX_MESSAGE_DATA_BYTES`). */
|
|
5410
|
-
data?: Record<string, unknown>;
|
|
5411
|
-
ackedAt?: string;
|
|
5412
|
-
}
|
|
5413
|
-
declare const MAX_MESSAGE_DATA_BYTES: number;
|
|
5414
|
-
/** The slice of a session descriptor relation/ACL resolution needs. */
|
|
5415
|
-
interface MessageTreeNode {
|
|
5416
|
-
id: string;
|
|
5417
|
-
parentSessionId?: string;
|
|
5418
|
-
label?: string;
|
|
5419
|
-
role?: string;
|
|
5420
|
-
adapterSlug?: string;
|
|
6198
|
+
/** True when the install failed ONLY because a manifest setup step
|
|
6199
|
+
* declares `interactive: true` and this process has no TTY to run it
|
|
6200
|
+
* in (e.g. openclaw's `onboard --install-daemon` TUI). The remedy is a
|
|
6201
|
+
* real terminal — `agentproto setup <slug>` — which a UI can offer
|
|
6202
|
+
* directly as a PTY terminal session. */
|
|
6203
|
+
needsInteractiveSetup?: boolean;
|
|
5421
6204
|
}
|
|
5422
6205
|
/**
|
|
5423
|
-
*
|
|
5424
|
-
*
|
|
5425
|
-
*
|
|
5426
|
-
*
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
/**
|
|
5430
|
-
* ACL: child→parent, parent→direct child, human/system→any. Sibling↔sibling
|
|
5431
|
-
* only when `allowSiblings` (config-gated, default off). Anything else — a
|
|
5432
|
-
* cousin, a stranger, yourself — is refused.
|
|
6206
|
+
* Installs an agent CLI adapter by slug and reports the outcome. Wired by
|
|
6207
|
+
* the daemon from `@agentproto/cli`'s `installAdapter` (which knows the
|
|
6208
|
+
* catalog + the two install classes); the runtime stays cli-free and only
|
|
6209
|
+
* exposes the verb. Must never throw for an ordinary install failure —
|
|
6210
|
+
* report it via `ok:false` + `message` — so the MCP tool / HTTP route can
|
|
6211
|
+
* return a clean result instead of a 500.
|
|
5433
6212
|
*/
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
6213
|
+
type AgentAdapterInstaller = (slug: string) => Promise<AdapterInstallResult>;
|
|
6214
|
+
/** Loads the read-only vendor/product/route catalog (SPEC §5) for
|
|
6215
|
+
* `GET /catalog/models` + the `catalog_models` MCP tool. A host wires this
|
|
6216
|
+
* from `buildCatalogModels` (`catalog-models.ts`) fed by its installed
|
|
6217
|
+
* adapters + `@agentproto/auth`'s `listAuthProfiles()` — the query params
|
|
6218
|
+
* are forwarded verbatim from the request. Omitted ⇒ the route/tool
|
|
6219
|
+
* report "not enabled" (same convention as `listAgentAdapters`). */
|
|
6220
|
+
type CatalogModelsLister = (query: CatalogModelsQuery) => Promise<CatalogModelsResponse>;
|
|
6221
|
+
interface AuthOptions {
|
|
6222
|
+
mode: "none" | "bearer";
|
|
6223
|
+
token?: string;
|
|
6224
|
+
}
|
|
6225
|
+
|
|
6226
|
+
/** One background task as mirrored onto `SessionDescriptor.backgroundTasks`. */
|
|
6227
|
+
interface SessionBackgroundTask {
|
|
6228
|
+
taskId: string;
|
|
6229
|
+
/** Friendly task type (`"shell"`, `"monitor"`, ...), when the agent says. */
|
|
6230
|
+
taskKind?: string;
|
|
6231
|
+
description?: string;
|
|
6232
|
+
/** Where the task writes its output, when the agent says. */
|
|
6233
|
+
outputFile?: string;
|
|
6234
|
+
status: "running" | "paused" | "completed" | "failed" | "stopped";
|
|
6235
|
+
summary?: string;
|
|
6236
|
+
toolCallId?: string;
|
|
6237
|
+
/** ISO 8601 — when the daemon first heard of the task. */
|
|
6238
|
+
startedAt: string;
|
|
5456
6239
|
}
|
|
5457
|
-
declare function matchesMessageFilter(msg: SessionMessage, filter: MessageFilter): boolean;
|
|
5458
|
-
declare const MESSAGE_TAG = "agentproto-message";
|
|
5459
|
-
/** Child-settable label → attribute-safe: `[A-Za-z0-9._-]`, ≤ 48 chars. */
|
|
5460
|
-
declare function sanitizeLabel(label: string | undefined): string | undefined;
|
|
5461
|
-
/** Escape every envelope/body sentinel inside a message body, so a sender
|
|
5462
|
-
* can't close the tag early and forge a second header. */
|
|
5463
|
-
declare function escapeMessageBody(text: string): string;
|
|
5464
|
-
/** A human prompt is NOT wrapped (humans keep today's UX), but a line that
|
|
5465
|
-
* starts with the envelope sentinel is escaped, so the invariant "text
|
|
5466
|
-
* outside an `<agentproto-message>` tag is the human" holds. Nothing else
|
|
5467
|
-
* in human text changes. */
|
|
5468
|
-
declare function escapeHumanPrompt(text: string): string;
|
|
5469
|
-
declare function renderSessionMessage(msg: SessionMessage): string;
|
|
5470
|
-
/** A coalesced batch → sibling tags, one per message, in order. */
|
|
5471
|
-
declare function renderSessionMessages(msgs: readonly SessionMessage[]): string;
|
|
5472
|
-
/** One-line typed digest of `fyi` messages that never woke the session —
|
|
5473
|
-
* prepended (as a `system-prompt` slice) to the next turn it runs anyway. */
|
|
5474
|
-
declare function renderInboxDigest(msgs: readonly SessionMessage[]): string;
|
|
5475
|
-
/** Taught once, the first time a session RECEIVES a message (recorded as a
|
|
5476
|
-
* `system-prompt`). Children also get the same invariant in their lineage
|
|
5477
|
-
* line at spawn. */
|
|
5478
|
-
declare const MESSAGE_PREAMBLE: string;
|
|
5479
6240
|
|
|
5480
6241
|
/**
|
|
5481
6242
|
* A per-session observer — the pluggable seam behind the runtime's transcript
|
|
@@ -5537,6 +6298,94 @@ interface SessionObserver {
|
|
|
5537
6298
|
*/
|
|
5538
6299
|
declare function composeSessionObservers(observers: readonly SessionObserver[]): SessionObserver;
|
|
5539
6300
|
|
|
6301
|
+
/**
|
|
6302
|
+
* Session-scoped artifact store — durable, content-addressed documents
|
|
6303
|
+
* (image, pdf, html, presentation, site, generic file) attached to an
|
|
6304
|
+
* agent-cli session, kept across restarts and browsable in the session's
|
|
6305
|
+
* "Artifacts" section, distinct from the ephemeral inline card a chat
|
|
6306
|
+
* message renders.
|
|
6307
|
+
*
|
|
6308
|
+
* Deliberately mirrors `transcript-writer.ts`'s `AttachmentEntry`, which
|
|
6309
|
+
* itself mirrors AIP-58's `ArtifactEntry` (`packages/workflow-runtime/src/
|
|
6310
|
+
* types.ts`) field-for-field — `key`/`path`/`sha256`/`size`/`contentType` —
|
|
6311
|
+
* so promoting an attachment to an artifact is a metadata copy, never a
|
|
6312
|
+
* data migration. What this module adds on top of a bare `ArtifactEntry`
|
|
6313
|
+
* is the session-artifact-specific envelope AIP-58 has no notion of:
|
|
6314
|
+
* `kind`, `label`, `pinned`, `createdBy`, `sourceRef`, and version history
|
|
6315
|
+
* under one stable `key`.
|
|
6316
|
+
*
|
|
6317
|
+
* Storage layout, under `sessionTranscriptDir(sessionId)`:
|
|
6318
|
+
*
|
|
6319
|
+
* artifacts/<sha256>.<ext> — one file per single-file version
|
|
6320
|
+
* artifacts/<sha256>/ — one directory per "site" version
|
|
6321
|
+
* artifacts.jsonl — append-only ledger, one JSON line per
|
|
6322
|
+
* mutation (`artifact.added` |
|
|
6323
|
+
* `artifact.pinned`)
|
|
6324
|
+
*
|
|
6325
|
+
* The ledger is the single source of truth, same posture AIP-58 §5 takes
|
|
6326
|
+
* for its run event log: `listSessionArtifacts`/`getSessionArtifact` are
|
|
6327
|
+
* folds over it, never a second store that could disagree with it. A
|
|
6328
|
+
* `key`'s version history is append-only; nothing here ever rewrites a
|
|
6329
|
+
* prior version's file or ledger line.
|
|
6330
|
+
*/
|
|
6331
|
+
type ArtifactKind = "image" | "document" | "pdf" | "html" | "presentation" | "site" | "file";
|
|
6332
|
+
type ArtifactCreator = "agent" | "user";
|
|
6333
|
+
interface ArtifactVersionEntry {
|
|
6334
|
+
version: number;
|
|
6335
|
+
/** Content hash — sha256 of the file's bytes for a single-file version,
|
|
6336
|
+
* or a manifest hash (sorted relative paths + sizes) for a "site"
|
|
6337
|
+
* directory version. Not a cryptographic content-integrity guarantee
|
|
6338
|
+
* for the directory case, only a stable change-detection key. */
|
|
6339
|
+
sha256: string;
|
|
6340
|
+
/** Relative to `sessionTranscriptDir(sessionId)` — `"artifacts/<sha>.<ext>"`
|
|
6341
|
+
* for a single file, `"artifacts/<sha>"` (a directory) for a site. */
|
|
6342
|
+
path: string;
|
|
6343
|
+
/** True when `path` names a directory (a "site"). */
|
|
6344
|
+
isDirectory: boolean;
|
|
6345
|
+
size: number;
|
|
6346
|
+
contentType?: string;
|
|
6347
|
+
createdAt: string;
|
|
6348
|
+
createdBy: ArtifactCreator;
|
|
6349
|
+
sourceRef?: string;
|
|
6350
|
+
}
|
|
6351
|
+
interface ArtifactRecord {
|
|
6352
|
+
key: string;
|
|
6353
|
+
kind: ArtifactKind;
|
|
6354
|
+
label?: string;
|
|
6355
|
+
pinned: boolean;
|
|
6356
|
+
updatedAt: string;
|
|
6357
|
+
versions: ArtifactVersionEntry[];
|
|
6358
|
+
}
|
|
6359
|
+
interface AddSessionArtifactInput {
|
|
6360
|
+
/** Stable id this version is filed under. Versions accrue when the same
|
|
6361
|
+
* key is added again with different content. Defaults to a slug of
|
|
6362
|
+
* `name`, else the content hash. */
|
|
6363
|
+
key?: string;
|
|
6364
|
+
kind?: ArtifactKind;
|
|
6365
|
+
label?: string;
|
|
6366
|
+
createdBy: ArtifactCreator;
|
|
6367
|
+
/** e.g. `"attachment:attachments/<sha>.<ext>"`, `"workflow-run:<runId>/
|
|
6368
|
+
* <artifactKey>"`, `"canvakit:<exportPath>"` — free-form provenance. */
|
|
6369
|
+
sourceRef?: string;
|
|
6370
|
+
/** Inline content, base64-encoded. Mutually exclusive with `sourcePath`. */
|
|
6371
|
+
bytes?: string;
|
|
6372
|
+
/** Display name — also the default `key`/extension source. */
|
|
6373
|
+
name?: string;
|
|
6374
|
+
mimeType?: string;
|
|
6375
|
+
/** Absolute path to an existing file or directory (e.g. a canvakit
|
|
6376
|
+
* export) to copy into the store. A directory implies `kind: "site"`
|
|
6377
|
+
* unless overridden. Mutually exclusive with `bytes`. */
|
|
6378
|
+
sourcePath?: string;
|
|
6379
|
+
}
|
|
6380
|
+
interface GetSessionArtifactResult {
|
|
6381
|
+
record: ArtifactRecord;
|
|
6382
|
+
version: ArtifactVersionEntry;
|
|
6383
|
+
/** Absent for a "site" (directory) version — browse it via the raw-serve
|
|
6384
|
+
* route instead; nothing here bounds a whole directory's bytes. */
|
|
6385
|
+
content?: Buffer;
|
|
6386
|
+
truncated: boolean;
|
|
6387
|
+
}
|
|
6388
|
+
|
|
5540
6389
|
/** Where a session's `contextSize` came from — see the module doc. */
|
|
5541
6390
|
type ContextSizeSource = "adapter" | "catalog" | "reported";
|
|
5542
6391
|
|
|
@@ -5560,8 +6409,11 @@ type ContextSizeSource = "adapter" | "catalog" | "reported";
|
|
|
5560
6409
|
*/
|
|
5561
6410
|
|
|
5562
6411
|
interface SessionOutcomeArtifact {
|
|
5563
|
-
type: "pr" | "commit" | "url";
|
|
5564
|
-
/** The artifact's canonical reference — a PR url, a commit sha, a url
|
|
6412
|
+
type: "pr" | "commit" | "url" | "file";
|
|
6413
|
+
/** The artifact's canonical reference — a PR url, a commit sha, a url,
|
|
6414
|
+
* or (for `type: "file"`) the session artifact's `key`
|
|
6415
|
+
* (`session-artifacts.ts`) — resolve it via `session_artifact_get` /
|
|
6416
|
+
* `GET /sessions/:id/artifacts/:key`. */
|
|
5565
6417
|
ref: string;
|
|
5566
6418
|
title?: string;
|
|
5567
6419
|
}
|
|
@@ -5574,12 +6426,26 @@ interface SessionOutcomeLink {
|
|
|
5574
6426
|
title?: string;
|
|
5575
6427
|
}
|
|
5576
6428
|
interface SessionOutcome {
|
|
5577
|
-
/** Level 1 only ever writes `"derived"
|
|
5578
|
-
*
|
|
5579
|
-
|
|
6429
|
+
/** Level 1 (`deriveSessionOutcome`) only ever writes `"derived"` — what an
|
|
6430
|
+
* ended session produced, with zero agent/human cooperation. Level 2 adds
|
|
6431
|
+
* `"judged"` (a judge agent decided the verdict, `judgedBy` names the
|
|
6432
|
+
* judge session) and `"declared"` (a deterministic rule or an operator
|
|
6433
|
+
* declared it, `judgedBy` is `"steward-rules"` or absent). See
|
|
6434
|
+
* `registry.closeWithOutcome`. */
|
|
6435
|
+
source: "derived" | "judged" | "declared";
|
|
5580
6436
|
/** `produced` — the session said something or left an artifact;
|
|
5581
6437
|
* `empty` — no assistant text and no artifacts. */
|
|
5582
6438
|
status: "produced" | "empty";
|
|
6439
|
+
/** Level 2 only: what a judge/declaration decided the session's work
|
|
6440
|
+
* amounted to. Absent on a plain Level 1 `"derived"` outcome. */
|
|
6441
|
+
verdict?: "done" | "abandoned" | "blocked" | "needs-input";
|
|
6442
|
+
/** Level 2 only: who reached `verdict` — a session id (a judge agent) or
|
|
6443
|
+
* the literal `"steward-rules"` for a deterministic close with no judge
|
|
6444
|
+
* in the loop. Absent on a plain Level 1 `"derived"` outcome. */
|
|
6445
|
+
judgedBy?: string;
|
|
6446
|
+
/** Level 2 only: free-text note from the judge/declaration explaining
|
|
6447
|
+
* `verdict` — never populated by `deriveSessionOutcome` itself. */
|
|
6448
|
+
note?: string;
|
|
5583
6449
|
/** Last assistant message of the session, trimmed to
|
|
5584
6450
|
* {@link OUTCOME_SUMMARY_MAX} chars (the TAIL is kept — the conclusion,
|
|
5585
6451
|
* not the preamble). Absent when the session never said anything. */
|
|
@@ -5625,6 +6491,12 @@ interface SessionOutcomeCompact {
|
|
|
5625
6491
|
interface DeriveSessionOutcomeInput {
|
|
5626
6492
|
/** The last assistant message the registry observed (raw, untrimmed). */
|
|
5627
6493
|
lastAssistantText?: string;
|
|
6494
|
+
/** Extra `type: "file"` artifacts to merge alongside the PR-derived ones
|
|
6495
|
+
* — the registry passes the session's PINNED `session-artifacts.ts`
|
|
6496
|
+
* records here so the ended block can show them (an unpinned artifact
|
|
6497
|
+
* still lists in the session's "Artifacts" section, just not surfaced
|
|
6498
|
+
* in the terse outcome). */
|
|
6499
|
+
artifacts?: SessionOutcomeArtifact[];
|
|
5628
6500
|
now?: Date;
|
|
5629
6501
|
}
|
|
5630
6502
|
/**
|
|
@@ -6155,7 +7027,10 @@ interface SessionDescriptor {
|
|
|
6155
7027
|
* `"parent-exited"` (an orchestrator's dying subtree reap),
|
|
6156
7028
|
* `"provider-limit"` (a driver-reported subscription/usage-cap error,
|
|
6157
7029
|
* e.g. Claude Code's "hit your session limit"), `"forgotten"` (a live
|
|
6158
|
-
* session killed as part of an operator `DELETE`)
|
|
7030
|
+
* session killed as part of an operator `DELETE`), `"steward-completed"` /
|
|
7031
|
+
* `"steward-abandoned"` (the session steward's `closeWithOutcome` —
|
|
7032
|
+
* verdict `"done"` vs. `"abandoned"`; a `"blocked"`/`"needs-input"`
|
|
7033
|
+
* verdict doesn't close at all, see `SessionDescriptor.wrapupFlag`).
|
|
6159
7034
|
* Absent for every terminal path this file doesn't tag (a plain natural
|
|
6160
7035
|
* exit, an ordinary turn error) — the session's own fault, or at least
|
|
6161
7036
|
* not something worth a special label. A string outside this list is
|
|
@@ -6188,6 +7063,20 @@ interface SessionDescriptor {
|
|
|
6188
7063
|
* revives the row. Persisted, so it survives restarts and archiving. See
|
|
6189
7064
|
* `session-outcome.ts`. */
|
|
6190
7065
|
outcome?: SessionOutcome;
|
|
7066
|
+
/** A steward verdict of `"blocked"` or `"needs-input"` from
|
|
7067
|
+
* `registry.closeWithOutcome` (`session-wrapup.ts` / FIX-9A) — recorded
|
|
7068
|
+
* INSTEAD of closing, since neither verdict means the session is done.
|
|
7069
|
+
* The session stays exactly as alive as it was; this is a signal for a
|
|
7070
|
+
* human/orchestrator to look, not a termination. Persisted; overwritten
|
|
7071
|
+
* (not accumulated) by the next flag, and left stale on the descriptor
|
|
7072
|
+
* until something clears it (nothing here does — a later successful
|
|
7073
|
+
* turn/close is a separate, more informative signal than deleting this). */
|
|
7074
|
+
wrapupFlag?: {
|
|
7075
|
+
verdict: "blocked" | "needs-input";
|
|
7076
|
+
note?: string;
|
|
7077
|
+
judgedBy?: string;
|
|
7078
|
+
at: string;
|
|
7079
|
+
};
|
|
6191
7080
|
/** Last time anything was written to stdout/stderr. Lets the UI
|
|
6192
7081
|
* spot stuck sessions ("running for 2h, last output 12min ago"). */
|
|
6193
7082
|
lastOutputAt?: string;
|
|
@@ -6225,6 +7114,12 @@ interface SessionDescriptor {
|
|
|
6225
7114
|
* every read since it's a live OS query, stale the instant it's
|
|
6226
7115
|
* written to disk. */
|
|
6227
7116
|
processAlive?: boolean;
|
|
7117
|
+
/** Summed RSS in bytes of this session's process and all its OS-level
|
|
7118
|
+
* descendants (`processTreeRss`, `process-memory.ts`). Ephemeral,
|
|
7119
|
+
* never persisted — stamped only when a caller opts in (`session_list`'s
|
|
7120
|
+
* `withMemory: true`) since it costs a `ps` spawn; absent otherwise, and
|
|
7121
|
+
* absent for a row with no `pid` or that `ps` didn't report. */
|
|
7122
|
+
rssBytes?: number;
|
|
6228
7123
|
/** Count of live supervisors currently blocked waiting on this session —
|
|
6229
7124
|
* HTTP `GET /sessions/:id/wait` long-polls and `session_monitor`
|
|
6230
7125
|
* subscriptions, both via `monitorSessionWait` (#session-visibility).
|
|
@@ -6320,16 +7215,32 @@ interface SessionDescriptor {
|
|
|
6320
7215
|
* `status` to `"error"` directly (a genuinely terminal row the existing
|
|
6321
7216
|
* crash/error handling already catches). This is the twin gap for a
|
|
6322
7217
|
* turn that fails IN-BAND while the process stays alive: `status` stays
|
|
6323
|
-
* `"running"`, `lastError` stays unset (reserved for `markCrashed`
|
|
6324
|
-
*
|
|
6325
|
-
*
|
|
6326
|
-
*
|
|
6327
|
-
*
|
|
6328
|
-
*
|
|
6329
|
-
*
|
|
6330
|
-
*
|
|
7218
|
+
* `"running"`, `lastError` stays unset (still reserved for `markCrashed`
|
|
7219
|
+
* — see `lastTurnErrorMessage` below for the message text this field's
|
|
7220
|
+
* own turn carries), and nothing else distinguishes the session from
|
|
7221
|
+
* one that simply finished a clean turn and is idle. Folded into the
|
|
7222
|
+
* same "stalled" activity/UI treatment a mid-turn stall gets (see
|
|
7223
|
+
* sessionsTree.logic.ts's `activityFor`) rather than a new state, on
|
|
7224
|
+
* the theory that both mean "the last thing this session did needs a
|
|
7225
|
+
* look, not a re-prompt on faith". Stamped at turn-end when
|
|
7226
|
+
* `turnEndReason === "error"` — including the case where the adapter's
|
|
7227
|
+
* stream generator simply returned after emitting an in-band `error`
|
|
7228
|
+
* event with no explicit turn-end and no thrown exception, which
|
|
7229
|
+
* `runAgentTurn`'s P5 synthetic-reason fallback now classifies as
|
|
7230
|
+
* `"error"` rather than defaulting to `"exited"`. Cleared the moment a
|
|
7231
|
+
* LATER turn completes without one (same reset shape as
|
|
6331
7232
|
* `resumeAttempts`/`restartAttempts`). Detection + signal only. */
|
|
6332
7233
|
lastTurnErroredAt?: string;
|
|
7234
|
+
/** The captured error text for the turn `lastTurnErroredAt` timestamps —
|
|
7235
|
+
* `evt.error.message` off the in-band stream `error` event observed
|
|
7236
|
+
* during that turn (see `runAgentTurn`'s `turnErrorMessage`). Distinct
|
|
7237
|
+
* from `lastError` (reserved for `markCrashed`) and from
|
|
7238
|
+
* `rt.lastErrorMessage` (an internal, cross-turn, never-cleared runtime
|
|
7239
|
+
* field — this one is the public, turn-scoped descriptor projection of
|
|
7240
|
+
* it). Absent when the adapter reported `reason:"error"` on its own
|
|
7241
|
+
* turn-end without a preceding `error`-kind stream event. Stamped and
|
|
7242
|
+
* cleared together with `lastTurnErroredAt`. */
|
|
7243
|
+
lastTurnErrorMessage?: string;
|
|
6333
7244
|
/** True when the LAST completed turn produced zero assistant output and
|
|
6334
7245
|
* zero tool calls (mirrors `SessionTurnEndEvent.empty` — see that
|
|
6335
7246
|
* field's doc). Persisted so `monitorSessionWait`'s synchronous
|
|
@@ -6359,6 +7270,32 @@ interface SessionDescriptor {
|
|
|
6359
7270
|
* the underlying `killedMidTurn`/`endedReason`). The in-place resume path
|
|
6360
7271
|
* never auto-retries the interrupted prompt. */
|
|
6361
7272
|
interrupted?: boolean;
|
|
7273
|
+
/** The `SessionsRegistry.bootId` of the daemon boot that followed the
|
|
7274
|
+
* restart which cut this row's turn off. The graceful shutdown that
|
|
7275
|
+
* interrupts a turn parks `INTERRUPTED_PENDING_BOOT` here; the next boot
|
|
7276
|
+
* reload (`loadHistorySnapshot`) swaps that — or, after an unclean exit, a
|
|
7277
|
+
* row still alive on disk — for its own `bootId`. Nothing else is ever
|
|
7278
|
+
* stamped: an interrupted row carrying no marker was persisted by a daemon
|
|
7279
|
+
* predating it, i.e. by an older restart, and stays stale. Cleared on the
|
|
7280
|
+
* next successful turn-end (with the markers it
|
|
7281
|
+
* qualifies). `interruptedAtBoot === registry.bootId` is how the
|
|
7282
|
+
* continue-interrupted verb and boot pass pick "interrupted by the LAST
|
|
7283
|
+
* restart" apart from a stale interruption nobody picked back up. PERSISTED
|
|
7284
|
+
* (unlike the derived `interrupted`) — the boot that stamped it may not be
|
|
7285
|
+
* the one reading it. */
|
|
7286
|
+
interruptedAtBoot?: string;
|
|
7287
|
+
/** How many automatic continue prompts the daemon has sent this row
|
|
7288
|
+
* (`daemon.continueInterruptedOnBoot`) since its last successful turn-end.
|
|
7289
|
+
* Incremented BEFORE the prompt is sent, so a continue turn that itself
|
|
7290
|
+
* gets cut off by another restart still counts; reset on the next
|
|
7291
|
+
* successful turn-end. The boot pass stops at
|
|
7292
|
+
* `MAX_AUTO_CONTINUE_ATTEMPTS` — the no-loop bound for a turn that keeps
|
|
7293
|
+
* taking the daemon down with it. PERSISTED; absent until the first one. */
|
|
7294
|
+
autoContinueAttempts?: number;
|
|
7295
|
+
/** `bootId` of the boot whose pass last auto-continued this row — the
|
|
7296
|
+
* at-most-one-auto-continue-per-restart guard. Reset with
|
|
7297
|
+
* `autoContinueAttempts`. */
|
|
7298
|
+
lastAutoContinueBoot?: string;
|
|
6362
7299
|
/** How many in-place resume attempts have FAILED in a row for this session
|
|
6363
7300
|
* (§5 cap/backoff). Incremented on every failed `maybeResumeAgent` attempt —
|
|
6364
7301
|
* the resume throws, the adapter rejects the id, or the spawn returns null —
|
|
@@ -6717,6 +7654,19 @@ interface SessionDescriptor {
|
|
|
6717
7654
|
/** Cumulative input / output token counts (same source + cadence as costUsd). */
|
|
6718
7655
|
tokensIn?: number;
|
|
6719
7656
|
tokensOut?: number;
|
|
7657
|
+
/** Cumulative prompt-cache read / cache-creation / reasoning tokens, from
|
|
7658
|
+
* the adapter's usage reader (claude-code transcript, hermes state.db,
|
|
7659
|
+
* opencode.db). Absent when the adapter doesn't expose them — never
|
|
7660
|
+
* zero-filled. See `UsageDetail` in usage.ts. */
|
|
7661
|
+
cacheReadTokens?: number;
|
|
7662
|
+
cacheWriteTokens?: number;
|
|
7663
|
+
reasoningTokens?: number;
|
|
7664
|
+
/** Tool calls the daemon observed across completed turns (cumulative).
|
|
7665
|
+
* Stamped at the first turn end, so absent = no turn has finished yet. */
|
|
7666
|
+
toolCalls?: number;
|
|
7667
|
+
/** Wall time spent inside turns, cumulative ms. Same cadence as
|
|
7668
|
+
* `toolCalls`. */
|
|
7669
|
+
durationMs?: number;
|
|
6720
7670
|
/** Context-window size + tokens-in-context from the latest `usage_update`
|
|
6721
7671
|
* event (ACP adapters that report a context window). Refreshed live as
|
|
6722
7672
|
* usage_update events arrive, not just at turn-end. */
|
|
@@ -6776,6 +7726,20 @@ interface SessionDescriptor {
|
|
|
6776
7726
|
* — so this carries no secrets today. Should the shape ever grow
|
|
6777
7727
|
* headers/tokens, NEVER log this field's contents. */
|
|
6778
7728
|
mcpServers?: AcpMcpServer[];
|
|
7729
|
+
/** Resolved skills list at spawn time — explicit ∪ preset ∪ config
|
|
7730
|
+
* defaults (`resolveSpawnDefaults`'s own merge), recorded regardless of
|
|
7731
|
+
* whether the adapter actually CONSUMES a `skills` spawn option (today
|
|
7732
|
+
* only hermes does — see `normalizeSkillsOption`). Persisted so it
|
|
7733
|
+
* survives restart/resume like `mcpServers`, and so a read surface
|
|
7734
|
+
* (`session_capabilities`) can show what was resolved even for an
|
|
7735
|
+
* adapter (claude-code) that auto-discovers skills on its own and never
|
|
7736
|
+
* reads this list back. */
|
|
7737
|
+
skills?: string[];
|
|
7738
|
+
/** Capability bundle ids (`bundle_list`) resolved for this spawn (PLAN D
|
|
7739
|
+
* phase 1) — ids only, never the expanded imported-MCP snapshots (those
|
|
7740
|
+
* live only in `mcpServers` above, as reference-only proxy URLs). Absent
|
|
7741
|
+
* ⇒ no bundle was attached. */
|
|
7742
|
+
bundles?: string[];
|
|
6779
7743
|
/** Provider-specific resume hints sniffed from the session's
|
|
6780
7744
|
* output. claude-code prints `claude --resume <uuid>` on exit;
|
|
6781
7745
|
* we capture that uuid as `claudeResumeId`. On `restart`, when a
|
|
@@ -6878,6 +7842,12 @@ interface SessionDescriptor {
|
|
|
6878
7842
|
* want. Threaded like `permissionHold` — set at spawn time, immutable
|
|
6879
7843
|
* thereafter. */
|
|
6880
7844
|
notifyParentOnCrash?: boolean;
|
|
7845
|
+
/** Per-spawn opt-out of sentinel auto-link (AIP-60 §6/step 4,
|
|
7846
|
+
* `agent_start.sentinel: false`). `undefined`/`true` = allowed (subject to
|
|
7847
|
+
* `config.sentinel.autoWatchPrs`); `false` = this session's opened PRs are
|
|
7848
|
+
* never auto-watched, no matter the config. Set once at spawn, immutable
|
|
7849
|
+
* thereafter — same threading as `notifyParentOnCrash`. */
|
|
7850
|
+
sentinelAutoWatch?: boolean;
|
|
6881
7851
|
/** True when the session was spawned in permission-hold mode. */
|
|
6882
7852
|
permissionHold?: boolean;
|
|
6883
7853
|
/** Effective OS-level confinement of the adapter's own process
|
|
@@ -6912,9 +7882,17 @@ interface SessionDescriptor {
|
|
|
6912
7882
|
* time (spawn / resume) — shown in `session_list` so a sender can predict
|
|
6913
7883
|
* a message's delivery tier. `steering`: the agent accepts ACP steering
|
|
6914
7884
|
* (`_session/steering`) — a `steer` message can be injected into its
|
|
6915
|
-
* running turn instead of waiting for it to end.
|
|
7885
|
+
* running turn instead of waiting for it to end. `commandsSupported`:
|
|
7886
|
+
* the live agent session is the ACP protocol arm (has a working `steer`
|
|
7887
|
+
* method — only the ACP arm implements it, per `AgentSessionLike`'s own
|
|
7888
|
+
* doc), which is what actually emits `available_commands_update`
|
|
7889
|
+
* notifications; false for the print/proprietary arms regardless of
|
|
7890
|
+
* `steeringSupported`, since a harness can be ACP without advertising
|
|
7891
|
+
* the steering extension. Read by `session_capabilities` to derive its
|
|
7892
|
+
* `arm`/`commandsSupported` fields without re-deriving the signal. */
|
|
6916
7893
|
capabilities?: {
|
|
6917
7894
|
steering: boolean;
|
|
7895
|
+
commandsSupported: boolean;
|
|
6918
7896
|
};
|
|
6919
7897
|
/** FIFO of prompts that arrived while this session was mid-turn and
|
|
6920
7898
|
* asked to be QUEUED rather than rejected (`enqueuePrompt`'s
|
|
@@ -7368,6 +8346,28 @@ type PermissionRespondResult = {
|
|
|
7368
8346
|
error: "not_found" | "session_gone" | "no_matching_option" | "unsupported";
|
|
7369
8347
|
message: string;
|
|
7370
8348
|
};
|
|
8349
|
+
/** Input to `SessionsRegistry.closeWithOutcome`. `verdict: "done" |
|
|
8350
|
+
* "abandoned"` records the Level 2 fields onto the session's
|
|
8351
|
+
* `SessionOutcome` and actually closes it; `"blocked" | "needs-input"`
|
|
8352
|
+
* records the SAME `note`/`judgedBy` onto `SessionDescriptor.wrapupFlag`
|
|
8353
|
+
* instead and never touches the session's liveness. See {@link
|
|
8354
|
+
* SessionOutcome} / {@link SessionDescriptor.wrapupFlag} for the field
|
|
8355
|
+
* meanings. */
|
|
8356
|
+
interface CloseWithOutcomeInput {
|
|
8357
|
+
verdict: "done" | "abandoned" | "blocked" | "needs-input";
|
|
8358
|
+
/** Overrides the outcome's derived summary when given (e.g. a judge's own
|
|
8359
|
+
* written summary) — trimmed the same way `deriveSessionOutcome` trims
|
|
8360
|
+
* `lastAssistantText`. Omitted keeps whatever `deriveSessionOutcome`
|
|
8361
|
+
* computed from the session's own last assistant message. Ignored for a
|
|
8362
|
+
* `"blocked"`/`"needs-input"` verdict (there's no outcome to summarize —
|
|
8363
|
+
* nothing closed). */
|
|
8364
|
+
summary?: string;
|
|
8365
|
+
note?: string;
|
|
8366
|
+
/** A judge session id, or `"steward-rules"` for a deterministic close with
|
|
8367
|
+
* no judge in the loop. */
|
|
8368
|
+
judgedBy?: string;
|
|
8369
|
+
source: "judged" | "declared";
|
|
8370
|
+
}
|
|
7371
8371
|
interface SessionsRegistry {
|
|
7372
8372
|
spawn(input: SpawnSessionInput): SessionDescriptor;
|
|
7373
8373
|
/** Adopt a ChildProcess that was spawned outside the registry —
|
|
@@ -7636,6 +8636,21 @@ interface SessionsRegistry {
|
|
|
7636
8636
|
reason?: string;
|
|
7637
8637
|
interrupted?: boolean;
|
|
7638
8638
|
}>;
|
|
8639
|
+
/** A currently-RUNNING background task's info plus a tail of its output
|
|
8640
|
+
* file, for the UI's "click a live background-task row to peek at its
|
|
8641
|
+
* output" affordance. Scoped to `SessionDescriptor.backgroundTasks` on
|
|
8642
|
+
* purpose — a settled task is dropped from that list moments after it
|
|
8643
|
+
* reports terminal status (see `noteBackgroundTask`), so this never
|
|
8644
|
+
* becomes an arbitrary-file-read: the caller supplies a `taskId`, never a
|
|
8645
|
+
* path, and only a path the agent itself already reported for a task
|
|
8646
|
+
* still tracked as running is ever read. Returns `null` when the session
|
|
8647
|
+
* or the task is unknown (the caller surfaces 404); `tail` is `null` when
|
|
8648
|
+
* the task has no `outputFile` or the file can't be read (not yet
|
|
8649
|
+
* created, already rotated away, ...). */
|
|
8650
|
+
readBackgroundTaskTail(id: string, taskId: string): {
|
|
8651
|
+
task: SessionBackgroundTask;
|
|
8652
|
+
tail: string | null;
|
|
8653
|
+
} | null;
|
|
7639
8654
|
/** Eagerly resume ONE dead-but-resumable agent-cli session IN PLACE,
|
|
7640
8655
|
* WITHOUT a prompt — the boot-time counterpart to the lazy resume that
|
|
7641
8656
|
* `sendPrompt`/`enqueuePrompt` trigger on the first prompt after a restart
|
|
@@ -7658,6 +8673,17 @@ interface SessionsRegistry {
|
|
|
7658
8673
|
* touching the adapter, so the bounded boot pass can call it per row and
|
|
7659
8674
|
* tally the results. */
|
|
7660
8675
|
resumeOnBoot(id: string): Promise<EagerResumeOutcome>;
|
|
8676
|
+
/** Opaque id unique to this registry instance — one per daemon boot.
|
|
8677
|
+
* Stamped as `interruptedAtBoot` on rows the boot reload finds interrupted,
|
|
8678
|
+
* so `interruptedAtBoot === bootId` means "interrupted by the restart that
|
|
8679
|
+
* preceded THIS boot" (continue-interrupted). */
|
|
8680
|
+
readonly bootId: string;
|
|
8681
|
+
/** Book one automatic continue prompt against `id` (increment
|
|
8682
|
+
* `autoContinueAttempts`, stamp `lastAutoContinueBoot = bootId`) and
|
|
8683
|
+
* persist. Called by the continue-on-boot pass BEFORE it sends, so the
|
|
8684
|
+
* no-loop bound holds even when the continue turn is itself cut off by the
|
|
8685
|
+
* next restart. No-op for an unknown id. */
|
|
8686
|
+
recordAutoContinue(id: string): void;
|
|
7661
8687
|
/** Cancel the in-flight turn on a live agent-cli session and leave the
|
|
7662
8688
|
* session itself alive and idle — the bare "interrupt, no next prompt"
|
|
7663
8689
|
* primitive `sendPrompt`/`enqueuePrompt`'s `opts.interrupt` arm lacks
|
|
@@ -7850,6 +8876,29 @@ interface SessionsRegistry {
|
|
|
7850
8876
|
* touches `keepAlive`, reaper eligibility, or any notification path —
|
|
7851
8877
|
* pin is quiet, structural state only. Throws when the id is unknown. */
|
|
7852
8878
|
setPinned(id: string, pinned: boolean): SessionDescriptor;
|
|
8879
|
+
/** Materialize a new artifact (or version of one) into the session's
|
|
8880
|
+
* durable artifact store (`session_artifact_add` MCP verb / `POST
|
|
8881
|
+
* /sessions/:id/artifacts`) — see `session-artifacts.ts`. Emits
|
|
8882
|
+
* `session:artifact-added` on a real (non-deduped) write. Throws when
|
|
8883
|
+
* the id is unknown. */
|
|
8884
|
+
addSessionArtifact(id: string, input: AddSessionArtifactInput): ArtifactRecord;
|
|
8885
|
+
/** Current-state fold of the session's artifact store — every key's
|
|
8886
|
+
* version history, most-recently-updated first. Empty array for a
|
|
8887
|
+
* known session with no artifacts yet. Throws when the id is unknown. */
|
|
8888
|
+
listSessionArtifacts(id: string): ArtifactRecord[];
|
|
8889
|
+
/** One artifact version's metadata plus its bounded file content (single-
|
|
8890
|
+
* file kinds only — a "site" version's content is browsed via the raw-
|
|
8891
|
+
* serve HTTP route instead). Undefined when the key/version doesn't
|
|
8892
|
+
* exist. Throws when the session id is unknown. */
|
|
8893
|
+
getSessionArtifact(id: string, key: string, opts?: {
|
|
8894
|
+
version?: number;
|
|
8895
|
+
maxBytes?: number;
|
|
8896
|
+
}): GetSessionArtifactResult | undefined;
|
|
8897
|
+
/** Set or clear an artifact's pin (`session_artifact_pin` MCP verb /
|
|
8898
|
+
* `POST /sessions/:id/artifacts/:key/pin`). Emits
|
|
8899
|
+
* `session:artifact-pinned-changed`. Undefined when the key doesn't
|
|
8900
|
+
* exist. Throws when the session id is unknown. */
|
|
8901
|
+
setArtifactPinned(id: string, key: string, pinned: boolean): ArtifactRecord | undefined;
|
|
7853
8902
|
/**
|
|
7854
8903
|
* Manually override `awaitingInput`/`awaitingQuestion` (the
|
|
7855
8904
|
* `session_flag_status` MCP verb) — the ONE write path for this pair
|
|
@@ -7955,6 +9004,34 @@ interface SessionsRegistry {
|
|
|
7955
9004
|
* reason (including `"operator-stopped"` or none) on a terminal row
|
|
7956
9005
|
* stays the plain no-op. */
|
|
7957
9006
|
kill(id: string, signal?: NodeJS.Signals, reason?: SessionEndReason): boolean;
|
|
9007
|
+
/** Close a LIVE agent-cli session with a Level 2 (judged/declared) outcome
|
|
9008
|
+
* — the primitive the session steward (FIX-9A/9B) drives once a wrap-up
|
|
9009
|
+
* plan (`planSessionWrapup`, `session-wrapup.ts`) puts a session in the
|
|
9010
|
+
* `close`/`stuck` class, or a judge agent reaches a verdict on a `judge`
|
|
9011
|
+
* one. Verdict `"done"` or `"abandoned"` actually closes: terminates the
|
|
9012
|
+
* same graceful way `kill()` does, tags `endedReason: "steward-completed"`
|
|
9013
|
+
* (`"done"`) or `"steward-abandoned"` (`"abandoned"`), and — like
|
|
9014
|
+
* `reapIdle`, unlike a plain `kill()` — CLEARS the in-memory
|
|
9015
|
+
* `agentSession` binding so the row stays lazy-resumable in place. The
|
|
9016
|
+
* recorded outcome's `source`/`verdict`/`judgedBy`/`note` are stamped onto
|
|
9017
|
+
* the row's `SessionOutcome` (see `session-outcome.ts`) on top of what
|
|
9018
|
+
* `deriveSessionOutcome` would otherwise compute (last assistant message,
|
|
9019
|
+
* PRs, cost) — a declared close still shows what the session actually
|
|
9020
|
+
* produced. Verdict `"blocked"` or `"needs-input"` is NOT a completion —
|
|
9021
|
+
* nothing is terminated; the verdict is recorded as
|
|
9022
|
+
* `SessionDescriptor.wrapupFlag` instead, and the session is left exactly
|
|
9023
|
+
* as alive as it was.
|
|
9024
|
+
*
|
|
9025
|
+
* Refuses (returns false, no-op) a session that is not a live
|
|
9026
|
+
* (`running`/`starting`) agent-cli row, or that AT THE MOMENT OF THE CALL
|
|
9027
|
+
* is `busy`/`awaitingInput`/`awaitingPermission` or has a background task
|
|
9028
|
+
* outstanding (`pendingBgTasks`/`backgroundTasks`) — a plan computed
|
|
9029
|
+
* moments earlier can be stale; this is the re-check that keeps an
|
|
9030
|
+
* autonomous close from ever landing on a session that just picked up a
|
|
9031
|
+
* turn, asked a question, or still has something in flight. Applies to
|
|
9032
|
+
* BOTH branches (close and flag-only) — a stale plan is refused either
|
|
9033
|
+
* way, never silently acted on. */
|
|
9034
|
+
closeWithOutcome(id: string, input: CloseWithOutcomeInput): boolean;
|
|
7958
9035
|
/** Retire a long-idle agent-cli session to free its adapter process — the
|
|
7959
9036
|
* primitive the idle-session reaper (`runIdleReapPass`, PR-6) drives on a
|
|
7960
9037
|
* periodic sweep. Terminates the underlying adapter/child the SAME graceful
|
|
@@ -8062,6 +9139,18 @@ interface SessionsRegistry {
|
|
|
8062
9139
|
* window keeps aging out naturally rather than being wiped by the
|
|
8063
9140
|
* give-up itself. Returns false (no-op) for an unknown id. */
|
|
8064
9141
|
giveUpRestart(id: string, message: string): boolean;
|
|
9142
|
+
/** Record a daemon-authored DISPLAY-ONLY notice into an agent-cli
|
|
9143
|
+
* session's transcript — the same `kind: "notice"` event `markCrashed`/
|
|
9144
|
+
* `giveUpRestart`/`interruptInFlightTurn` already stamp on their own
|
|
9145
|
+
* teardown/interrupt paths (see transcript-writer.ts's "notice" case),
|
|
9146
|
+
* exposed generically for a caller with just a session id, no `rt`
|
|
9147
|
+
* in hand. Appends to `events.jsonl` (so `session_story`/`live_session`
|
|
9148
|
+
* render it as a system message) and the ring buffer — nothing else:
|
|
9149
|
+
* it does NOT touch `promptQueue`, the inbox, or `busy`, so it can never
|
|
9150
|
+
* enqueue a prompt, wake an idle session into a turn, or interrupt a busy
|
|
9151
|
+
* one. Returns false (no-op) for an unknown id or a non-`agent-cli`
|
|
9152
|
+
* session. */
|
|
9153
|
+
recordNotice(id: string, text: string): boolean;
|
|
8065
9154
|
/** List permission requests currently parked in the pending-permissions
|
|
8066
9155
|
* inbox across all permission-hold sessions, newest last. Optionally
|
|
8067
9156
|
* filtered to one session. */
|
|
@@ -8167,8 +9256,10 @@ interface SpawnAgentInput {
|
|
|
8167
9256
|
/** Optional initial prompt to dispatch immediately. The promise
|
|
8168
9257
|
* the registry returns resolves AFTER the spawn — the prompt
|
|
8169
9258
|
* runs in the background, projecting events into the ring
|
|
8170
|
-
* buffer. Skip to spawn idle.
|
|
8171
|
-
|
|
9259
|
+
* buffer. Skip to spawn idle. A plain string, or a content-block
|
|
9260
|
+
* prompt (attachment-bearing first message) — see
|
|
9261
|
+
* `SpawnAgentSessionInput.prompt`'s doc. */
|
|
9262
|
+
initialPrompt?: string | Record<string, unknown> | unknown[];
|
|
8172
9263
|
/** The daemon-composed SYSTEM slice of {@link initialPrompt} — the part
|
|
8173
9264
|
* it synthesized ahead of the CALLER's ask (role disposition, lineage
|
|
8174
9265
|
* line, AGENTS.md, posture preamble). The adapter still receives the
|
|
@@ -8190,6 +9281,13 @@ interface SpawnAgentInput {
|
|
|
8190
9281
|
* the resume/re-spawn path can re-mount the same toolset (orchestrator
|
|
8191
9282
|
* WP1). */
|
|
8192
9283
|
mcpServers?: AcpMcpServer[];
|
|
9284
|
+
/** Resolved skills list at spawn time (`resolveSpawnDefaults`'s merged
|
|
9285
|
+
* explicit ∪ preset ∪ config-defaults result) — recorded verbatim onto
|
|
9286
|
+
* {@link SessionDescriptor.skills}. See that field's doc. */
|
|
9287
|
+
skills?: string[];
|
|
9288
|
+
/** Capability bundle ids resolved for this spawn — recorded verbatim onto
|
|
9289
|
+
* {@link SessionDescriptor.bundles}; see that field's doc. */
|
|
9290
|
+
bundles?: string[];
|
|
8193
9291
|
/** Persistent isolated-config dir this spawn passed to
|
|
8194
9292
|
* `startSession({ configDir })` — recorded verbatim onto
|
|
8195
9293
|
* {@link SessionDescriptor.adapterConfigDir} so restart/lazy-resume can
|
|
@@ -8260,12 +9358,7 @@ interface SpawnAgentInput {
|
|
|
8260
9358
|
* cost/token fields on the descriptor. Adapter-specific (e.g. hermes
|
|
8261
9359
|
* reads its state.db keyed by the adapter session id). Omit for adapters
|
|
8262
9360
|
* with no usage source. */
|
|
8263
|
-
readUsage?: () => Promise<
|
|
8264
|
-
model?: string;
|
|
8265
|
-
costUsd?: number;
|
|
8266
|
-
tokensIn?: number;
|
|
8267
|
-
tokensOut?: number;
|
|
8268
|
-
} | null>;
|
|
9361
|
+
readUsage?: () => Promise<UsageReadResult | null>;
|
|
8269
9362
|
/** Opt this session into Langfuse tracing (prompt/completion + tool spans +
|
|
8270
9363
|
* tokens/cost). Effective opt-in is `trace ?? opts.langfuseTracingDefault ?? false`. */
|
|
8271
9364
|
trace?: boolean;
|
|
@@ -8298,6 +9391,9 @@ interface SpawnAgentInput {
|
|
|
8298
9391
|
/** Recorded verbatim onto {@link SessionDescriptor.notifyParentOnCrash} —
|
|
8299
9392
|
* see that field's doc. Default false. */
|
|
8300
9393
|
notifyParentOnCrash?: boolean;
|
|
9394
|
+
/** Recorded verbatim onto {@link SessionDescriptor.sentinelAutoWatch} — see
|
|
9395
|
+
* that field's doc. `undefined` = allowed. */
|
|
9396
|
+
sentinelAutoWatch?: boolean;
|
|
8301
9397
|
/** Exempt this session from the idle-reaper (`isReapable` in
|
|
8302
9398
|
* idle-reaper.ts) regardless of how long it sits idle — stamped straight
|
|
8303
9399
|
* onto `SessionDescriptor.keepAlive`. Default false. */
|
|
@@ -8322,15 +9418,12 @@ type PendingAgentOutcome = {
|
|
|
8322
9418
|
commandPreview?: string;
|
|
8323
9419
|
resumable?: boolean;
|
|
8324
9420
|
nativeTerminalResume?: boolean;
|
|
8325
|
-
readUsage?: () => Promise<
|
|
8326
|
-
model?: string;
|
|
8327
|
-
costUsd?: number;
|
|
8328
|
-
tokensIn?: number;
|
|
8329
|
-
tokensOut?: number;
|
|
8330
|
-
} | null>;
|
|
9421
|
+
readUsage?: () => Promise<UsageReadResult | null>;
|
|
8331
9422
|
/** Dispatched now that the tree + driver session both exist — never
|
|
8332
|
-
* passed to `spawnAgentPending`, which would race the tree.
|
|
8333
|
-
|
|
9423
|
+
* passed to `spawnAgentPending`, which would race the tree. A plain
|
|
9424
|
+
* string, or a content-block prompt (attachment-bearing first
|
|
9425
|
+
* message) — see `SpawnAgentSessionInput.prompt`'s doc. */
|
|
9426
|
+
initialPrompt?: string | Record<string, unknown> | unknown[];
|
|
8334
9427
|
/** The daemon-composed SYSTEM slice of `initialPrompt` (see
|
|
8335
9428
|
* `SpawnAgentInput.initialPromptSystem`) — threaded through the
|
|
8336
9429
|
* placeholder so the deferred dispatch records it as a system turn. */
|
|
@@ -8606,6 +9699,170 @@ declare function runEagerResumePass(opts: {
|
|
|
8606
9699
|
isServed?: (desc: SessionDescriptor) => boolean;
|
|
8607
9700
|
}): Promise<EagerResumeSummary>;
|
|
8608
9701
|
|
|
9702
|
+
/**
|
|
9703
|
+
* Continue-interrupted: pick back up the sessions a daemon restart cut off
|
|
9704
|
+
* mid-turn, by sending each an explicit "continue" prompt.
|
|
9705
|
+
*
|
|
9706
|
+
* Everything below the prompt already exists — the persisted registry is the
|
|
9707
|
+
* snapshot, `maybeResumeAgent` resumes a dead row in place (lazily on its first
|
|
9708
|
+
* prompt, or eagerly at boot via `eager-resume.ts`), and the row keeps the
|
|
9709
|
+
* derived `interrupted` marker until its next successful turn-end. What a
|
|
9710
|
+
* restart does NOT do, by design, is re-run the interrupted prompt (prompts
|
|
9711
|
+
* aren't idempotent). This module is the explicit, opt-in replacement for that:
|
|
9712
|
+
* a NEW prompt that tells the agent its last turn was cut off and to check the
|
|
9713
|
+
* state on disk before carrying on.
|
|
9714
|
+
*
|
|
9715
|
+
* Two callers share one eligibility rule + one sender:
|
|
9716
|
+
* - the manual verb (`session_continue_interrupted` MCP tool,
|
|
9717
|
+
* `POST /sessions/continue-interrupted`, `agentproto sessions
|
|
9718
|
+
* continue-interrupted`) — `mode: "manual"`, dry-run by default;
|
|
9719
|
+
* - the opt-in boot pass (`daemon.continueInterruptedOnBoot`) —
|
|
9720
|
+
* `mode: "boot"`, run by serve.ts right after the eager resume pass.
|
|
9721
|
+
*
|
|
9722
|
+
* Eligibility (both modes): an agent-cli row that is `interrupted` BY THE LAST
|
|
9723
|
+
* RESTART (`interruptedAtBoot === registry.bootId` — a stale interruption from
|
|
9724
|
+
* an older restart that nobody picked up is left alone), resumable under the
|
|
9725
|
+
* attempt cap (`canResume`), not already busy, and whose `cwd` still exists
|
|
9726
|
+
* (a removed worktree fails the adapter respawn every time — knowable up front,
|
|
9727
|
+
* so the dry run must not call such a row eligible). The prompt goes through
|
|
9728
|
+
* `enqueuePrompt`, the same entry MCP `agent_prompt` uses, so a
|
|
9729
|
+
* dead-but-resumable row lazy-resumes with its billing auth re-resolved exactly
|
|
9730
|
+
* as a human prompt would. When that resume fails, the outcome says so instead
|
|
9731
|
+
* of passing on the bare post-resume "not alive" admission error.
|
|
9732
|
+
*
|
|
9733
|
+
* No-loop rules (boot mode only — a human pressing the button is intent):
|
|
9734
|
+
* - never auto-continue a row whose resume has failed (`resumeAttempts > 0`:
|
|
9735
|
+
* the eager pass, or an earlier attempt, could not bring it back);
|
|
9736
|
+
* - at most one auto-continue per restart (`lastAutoContinueBoot`);
|
|
9737
|
+
* - at most `MAX_AUTO_CONTINUE_ATTEMPTS` auto-continues without a successful
|
|
9738
|
+
* turn-end in between (`autoContinueAttempts`, booked BEFORE sending), so a
|
|
9739
|
+
* continue turn that keeps getting cut off — or keeps taking the daemon
|
|
9740
|
+
* down with it — stops being retried.
|
|
9741
|
+
*
|
|
9742
|
+
* Ordering (boot mode): MUST run after the supervisor is re-armed, same
|
|
9743
|
+
* constraint as `eager-resume.ts` — the continue turn's `turn-end` is exactly
|
|
9744
|
+
* what a re-armed completion policy is waiting for — and after the eager pass,
|
|
9745
|
+
* so a row whose eager resume failed is recognisable (`resumeAttempts`) and
|
|
9746
|
+
* skipped rather than lazily retried.
|
|
9747
|
+
*/
|
|
9748
|
+
|
|
9749
|
+
/** The prompt sent when the caller doesn't supply one. */
|
|
9750
|
+
declare const DEFAULT_CONTINUE_PROMPT: string;
|
|
9751
|
+
/** Transcript provenance for the continue prompt (`opts.source`). */
|
|
9752
|
+
declare const CONTINUE_PROMPT_SOURCE = "daemon:continue-interrupted";
|
|
9753
|
+
/** Cap on automatic continue prompts sent to one row without a successful
|
|
9754
|
+
* turn-end in between (boot mode). 2 = the original interruption gets one
|
|
9755
|
+
* auto-continue, and if a second restart cuts THAT turn off it gets one more;
|
|
9756
|
+
* after that only a human (manual verb) re-sends. */
|
|
9757
|
+
declare const MAX_AUTO_CONTINUE_ATTEMPTS = 2;
|
|
9758
|
+
type ContinueInterruptedSkipReason =
|
|
9759
|
+
/** Unknown id (explicit `ids` filter only). */
|
|
9760
|
+
"unknown"
|
|
9761
|
+
/** Not interrupted at all — no turn was cut off. */
|
|
9762
|
+
| "not-interrupted"
|
|
9763
|
+
/** Interrupted by an EARLIER restart, not the last one. */
|
|
9764
|
+
| "stale-interrupt"
|
|
9765
|
+
/** Not in-place resumable (not agent-cli / missing adapter session / archived). */
|
|
9766
|
+
| "not-resumable"
|
|
9767
|
+
/** Already burned through `MAX_RESUME_ATTEMPTS` failed resumes. */
|
|
9768
|
+
| "resume-cap-exhausted"
|
|
9769
|
+
/** Mid-turn already — someone got there first. */
|
|
9770
|
+
| "busy"
|
|
9771
|
+
/** The row's `cwd` is gone (removed worktree) — the resume would fail. */
|
|
9772
|
+
| "cwd-missing"
|
|
9773
|
+
/** Boot mode: a resume attempt has failed on this row. */
|
|
9774
|
+
| "resume-failed"
|
|
9775
|
+
/** Boot mode: already auto-continued by this boot. */
|
|
9776
|
+
| "already-auto-continued"
|
|
9777
|
+
/** Boot mode: `MAX_AUTO_CONTINUE_ATTEMPTS` reached without recovering. */
|
|
9778
|
+
| "auto-continue-cap"
|
|
9779
|
+
/** Excluded by the cross-process `isServed` gate (another daemon's row). */
|
|
9780
|
+
| "not-served";
|
|
9781
|
+
type ContinueInterruptedOutcome = {
|
|
9782
|
+
id: string;
|
|
9783
|
+
name?: string;
|
|
9784
|
+
status: "eligible";
|
|
9785
|
+
} | {
|
|
9786
|
+
id: string;
|
|
9787
|
+
name?: string;
|
|
9788
|
+
status: "sent";
|
|
9789
|
+
} | {
|
|
9790
|
+
id: string;
|
|
9791
|
+
name?: string;
|
|
9792
|
+
status: "skipped";
|
|
9793
|
+
reason: ContinueInterruptedSkipReason;
|
|
9794
|
+
} | {
|
|
9795
|
+
id: string;
|
|
9796
|
+
name?: string;
|
|
9797
|
+
status: "failed";
|
|
9798
|
+
error: string;
|
|
9799
|
+
};
|
|
9800
|
+
interface ContinueInterruptedResult {
|
|
9801
|
+
dryRun: boolean;
|
|
9802
|
+
/** The prompt that was (or, on a dry run, would be) sent. */
|
|
9803
|
+
prompt: string;
|
|
9804
|
+
/** The boot the candidates were interrupted before (`registry.bootId`). */
|
|
9805
|
+
bootId: string;
|
|
9806
|
+
/** One entry per considered row — every interrupted row when `ids` is
|
|
9807
|
+
* omitted, every requested id otherwise. Eligible-first ordering matches
|
|
9808
|
+
* the send order (newest activity first). */
|
|
9809
|
+
sessions: ContinueInterruptedOutcome[];
|
|
9810
|
+
/** Counts by outcome status. */
|
|
9811
|
+
eligible: number;
|
|
9812
|
+
sent: number;
|
|
9813
|
+
skipped: number;
|
|
9814
|
+
failed: number;
|
|
9815
|
+
}
|
|
9816
|
+
type ContinueInterruptedMode = "manual" | "boot";
|
|
9817
|
+
/** Decide one row. `undefined` ⇒ eligible. Pure — reads the descriptor only. */
|
|
9818
|
+
declare function continueSkipReason(desc: SessionDescriptor, bootId: string, mode: ContinueInterruptedMode): ContinueInterruptedSkipReason | undefined;
|
|
9819
|
+
/**
|
|
9820
|
+
* List — and unless `dryRun` — continue the sessions interrupted by the last
|
|
9821
|
+
* restart. See the module docblock for eligibility, the no-loop rules, and the
|
|
9822
|
+
* ordering contract. No-throw per row: a send that fails admission (row went
|
|
9823
|
+
* busy, resume refused) is reported as `failed` and the pass carries on.
|
|
9824
|
+
*/
|
|
9825
|
+
declare function continueInterruptedSessions(opts: {
|
|
9826
|
+
registry: Pick<SessionsRegistry, "list" | "get" | "enqueuePrompt" | "bootId" | "recordAutoContinue">;
|
|
9827
|
+
mode: ContinueInterruptedMode;
|
|
9828
|
+
/** Default true: report what would be sent, send nothing. */
|
|
9829
|
+
dryRun?: boolean;
|
|
9830
|
+
/** Restrict to these session ids. Omitted ⇒ every interrupted row. */
|
|
9831
|
+
ids?: readonly string[];
|
|
9832
|
+
/** Custom prompt. Omitted/blank ⇒ `DEFAULT_CONTINUE_PROMPT`. */
|
|
9833
|
+
prompt?: string;
|
|
9834
|
+
/** Max sends in flight at once (each may lazily resume an adapter). Clamped
|
|
9835
|
+
* to ≥1. Default 4, same as the eager pass. */
|
|
9836
|
+
concurrency?: number;
|
|
9837
|
+
/** Cross-process gate — see `runEagerResumePass`. Omitted ⇒ all served. */
|
|
9838
|
+
isServed?: (desc: SessionDescriptor) => boolean;
|
|
9839
|
+
/** Extra visibility filter (a scoped orchestrator's subtree). Rows it
|
|
9840
|
+
* rejects are dropped silently — never reported, never sent. */
|
|
9841
|
+
visible?: (desc: SessionDescriptor) => boolean;
|
|
9842
|
+
/** Filesystem probe for the `cwd-missing` check. Default `existsSync`. */
|
|
9843
|
+
cwdExists?: (cwd: string) => boolean;
|
|
9844
|
+
}): Promise<ContinueInterruptedResult>;
|
|
9845
|
+
/** Tally of the boot-time continue pass, for the boot-banner line. */
|
|
9846
|
+
interface ContinueOnBootSummary {
|
|
9847
|
+
/** False when `daemon.continueInterruptedOnBoot` is off (nothing ran). */
|
|
9848
|
+
enabled: boolean;
|
|
9849
|
+
/** Rows passing eligibility at selection time — the banner's denominator. */
|
|
9850
|
+
eligible: number;
|
|
9851
|
+
sent: number;
|
|
9852
|
+
skipped: number;
|
|
9853
|
+
failed: number;
|
|
9854
|
+
}
|
|
9855
|
+
/**
|
|
9856
|
+
* The boot pass: `continueInterruptedSessions` in `mode: "boot"`, not a dry
|
|
9857
|
+
* run, reduced to the banner tally. Caller (the gateway handle, invoked from
|
|
9858
|
+
* serve.ts) is responsible for the ordering contract in the module docblock.
|
|
9859
|
+
*/
|
|
9860
|
+
declare function runContinueOnBootPass(opts: {
|
|
9861
|
+
registry: Parameters<typeof continueInterruptedSessions>[0]["registry"];
|
|
9862
|
+
concurrency: number;
|
|
9863
|
+
isServed?: (desc: SessionDescriptor) => boolean;
|
|
9864
|
+
}): Promise<ContinueOnBootSummary>;
|
|
9865
|
+
|
|
8609
9866
|
/**
|
|
8610
9867
|
* Daemon-lane PR provenance STAMPER.
|
|
8611
9868
|
*
|
|
@@ -8811,6 +10068,89 @@ interface InboundWatcher {
|
|
|
8811
10068
|
shutdown(): void;
|
|
8812
10069
|
}
|
|
8813
10070
|
|
|
10071
|
+
/**
|
|
10072
|
+
* `capabilities_inventory` — one read that answers "what MCP servers and
|
|
10073
|
+
* skills does this daemon actually know about, and which harnesses can
|
|
10074
|
+
* reach them?" Backs both the `capabilities_inventory` MCP tool
|
|
10075
|
+
* (session-tools.ts) and its HTTP twin (`GET /capabilities/inventory`,
|
|
10076
|
+
* http-server.ts) so the two surfaces can never drift.
|
|
10077
|
+
*
|
|
10078
|
+
* Read-only, never throws: a failure in one source (MCP discovery, the
|
|
10079
|
+
* proxy registry, an adapter package that fails to import) becomes an
|
|
10080
|
+
* `error` string on that block alone — the rest of the inventory still
|
|
10081
|
+
* returns. Redaction: nothing here ever carries command/args/env/headers/
|
|
10082
|
+
* url — only the identity/status fields a UI needs (same discipline as
|
|
10083
|
+
* `compactDiscoveredMcp` / `compactImportedMcpEntry`).
|
|
10084
|
+
*
|
|
10085
|
+
* The `skills` block's per-adapter `metadata.skills` / `options` read uses
|
|
10086
|
+
* the same dynamic-`import()` + duck-typed-export technique as
|
|
10087
|
+
* `role-registry.ts`'s adapter-carried role discovery — reused rather than
|
|
10088
|
+
* reinvented, and importantly NOT a `packages/cli` dependency: it walks the
|
|
10089
|
+
* exact adapter list `listAgentAdapters` already returned (so `byHarness`
|
|
10090
|
+
* is guaranteed the same installed set as `adapter_list`), importing each
|
|
10091
|
+
* adapter's own already-installed package by its `packageName`.
|
|
10092
|
+
*/
|
|
10093
|
+
|
|
10094
|
+
interface CapabilitiesInventoryImportedMcp {
|
|
10095
|
+
id: string;
|
|
10096
|
+
alias?: string;
|
|
10097
|
+
name: string;
|
|
10098
|
+
type: string;
|
|
10099
|
+
source: string;
|
|
10100
|
+
status: "connected" | "idle" | "error" | "unknown";
|
|
10101
|
+
error?: string;
|
|
10102
|
+
/** Only present when `status === "connected"` — never connects just to count. */
|
|
10103
|
+
toolCount?: number;
|
|
10104
|
+
usedBySessions: string[];
|
|
10105
|
+
}
|
|
10106
|
+
interface CapabilitiesInventoryDiscoveredMcp {
|
|
10107
|
+
id: string;
|
|
10108
|
+
source: string;
|
|
10109
|
+
scope: string;
|
|
10110
|
+
name: string;
|
|
10111
|
+
type: string;
|
|
10112
|
+
imported: boolean;
|
|
10113
|
+
}
|
|
10114
|
+
interface CapabilitiesInventoryMcp {
|
|
10115
|
+
imported: CapabilitiesInventoryImportedMcp[];
|
|
10116
|
+
discovered: CapabilitiesInventoryDiscoveredMcp[];
|
|
10117
|
+
/** Which harnesses get the daemon's own `/mcp` (and so the imported pool)
|
|
10118
|
+
* mounted by default — derived from `shouldInjectDaemonSelfMount` +
|
|
10119
|
+
* each installed adapter's declared protocol, never a hardcoded list. */
|
|
10120
|
+
daemonMountByHarness: Record<string, "default" | "on-request" | "none">;
|
|
10121
|
+
error?: string;
|
|
10122
|
+
}
|
|
10123
|
+
interface SkillPackInventoryEntry {
|
|
10124
|
+
name: string;
|
|
10125
|
+
version?: string;
|
|
10126
|
+
path: string;
|
|
10127
|
+
skills: string[];
|
|
10128
|
+
}
|
|
10129
|
+
interface SkillsByHarnessEntry {
|
|
10130
|
+
adapter: string;
|
|
10131
|
+
target?: {
|
|
10132
|
+
format: string;
|
|
10133
|
+
dir: string;
|
|
10134
|
+
unit?: string;
|
|
10135
|
+
};
|
|
10136
|
+
installed: string[];
|
|
10137
|
+
native: string[];
|
|
10138
|
+
spawnOption: boolean;
|
|
10139
|
+
}
|
|
10140
|
+
interface CapabilitiesInventorySkills {
|
|
10141
|
+
packs: SkillPackInventoryEntry[];
|
|
10142
|
+
byHarness: SkillsByHarnessEntry[];
|
|
10143
|
+
defaults: {
|
|
10144
|
+
global: string[];
|
|
10145
|
+
byAdapter: Record<string, string[]>;
|
|
10146
|
+
};
|
|
10147
|
+
error?: string;
|
|
10148
|
+
}
|
|
10149
|
+
interface CapabilitiesInventory {
|
|
10150
|
+
mcp: CapabilitiesInventoryMcp;
|
|
10151
|
+
skills: CapabilitiesInventorySkills;
|
|
10152
|
+
}
|
|
10153
|
+
|
|
8814
10154
|
/**
|
|
8815
10155
|
* Dynamic MCP-app panels for installed `@agentproto/app-kit` apps that ship
|
|
8816
10156
|
* a `ui` block (`defineApp({ ui: { html, title?, tools?, csp? } })`,
|
|
@@ -8820,8 +10160,16 @@ interface InboundWatcher {
|
|
|
8820
10160
|
*
|
|
8821
10161
|
* `mcpServerFactory` (index.ts) rebuilds a fresh McpServer per `/mcp`
|
|
8822
10162
|
* request, so this module never throws — a bad app record must not take
|
|
8823
|
-
* down every other request.
|
|
8824
|
-
* `
|
|
10163
|
+
* down every other request. A collided tool id is still skipped with a
|
|
10164
|
+
* `console.warn` (there's nowhere else for a second panel to live). Every
|
|
10165
|
+
* OTHER failure — a removed app dir, a missing/unreadable `ui.path`, a
|
|
10166
|
+
* failed or in-flight `ui.build` — still mounts the panel, serving a
|
|
10167
|
+
* readable HTML placeholder/error page in place of the real bundle
|
|
10168
|
+
* (app-ui-placeholder.ts) instead of skipping it outright: a `ui://`
|
|
10169
|
+
* resource that silently doesn't exist just shows as a JSON-RPC "resource
|
|
10170
|
+
* not found" in a host, which is what motivated this file's placeholder
|
|
10171
|
+
* page in the first place — a HOST-VISIBLE page, even a stand-in one, beats
|
|
10172
|
+
* a resource the host has to explain away as missing.
|
|
8825
10173
|
*/
|
|
8826
10174
|
|
|
8827
10175
|
/**
|
|
@@ -9064,6 +10412,9 @@ interface PrStatusSnapshot {
|
|
|
9064
10412
|
name: string;
|
|
9065
10413
|
conclusion: string | null;
|
|
9066
10414
|
}>;
|
|
10415
|
+
/** The PR's head commit at fetch time — absent from snapshots taken
|
|
10416
|
+
* before this field existed. */
|
|
10417
|
+
headSha?: string;
|
|
9067
10418
|
}
|
|
9068
10419
|
/** The mutable annotations sidecar of a ledger entry. */
|
|
9069
10420
|
interface LedgerAnnotations {
|
|
@@ -9076,6 +10427,13 @@ interface ReviewLedgerFilter {
|
|
|
9076
10427
|
rangeSha?: string;
|
|
9077
10428
|
binding?: string;
|
|
9078
10429
|
manifestSha?: string;
|
|
10430
|
+
/** Keep only entries whose `attestation.requester.sessionId` is one of
|
|
10431
|
+
* these — backs `review_ledger({requesterSessionId, subtree})`: a single
|
|
10432
|
+
* id, or (with `subtree: true`) that session's whole subtree, resolved by
|
|
10433
|
+
* the caller before reaching the ledger. Answered from an in-memory
|
|
10434
|
+
* `requesterSessionId -> ledger keys` index (built lazily on first use,
|
|
10435
|
+
* kept current on every `put()`), never a full re-scan. */
|
|
10436
|
+
requesterSessionIds?: readonly string[];
|
|
9079
10437
|
}
|
|
9080
10438
|
interface ReviewLedger {
|
|
9081
10439
|
/** Absolute root directory of the ledger. */
|
|
@@ -9258,12 +10616,27 @@ interface ReviewRun {
|
|
|
9258
10616
|
/** Where the attestation was written in the ledger. */
|
|
9259
10617
|
ledgerPath?: string;
|
|
9260
10618
|
error?: string;
|
|
10619
|
+
/** The session that requested this run (`input.requesterSessionId ??
|
|
10620
|
+
* input.parentSessionId`), when known — mirrors
|
|
10621
|
+
* `Attestation.requester.sessionId` for a run that hasn't (or never will)
|
|
10622
|
+
* reach the ledger, so `review_ledger({includeRunning: true})` and the
|
|
10623
|
+
* `session_tree` badge (WP-review-panel) can attribute an in-flight/
|
|
10624
|
+
* cancelled/failed run to its requester without waiting for an
|
|
10625
|
+
* attestation. */
|
|
10626
|
+
requesterSessionId?: string;
|
|
9261
10627
|
}
|
|
9262
10628
|
interface ReviewRunner {
|
|
9263
10629
|
start(input: ReviewRunInput): ReviewRun;
|
|
9264
10630
|
status(runId: string): Promise<ReviewRun | undefined>;
|
|
9265
10631
|
wait(runId: string): Promise<ReviewRun | undefined>;
|
|
9266
10632
|
cancel(runId: string): boolean;
|
|
10633
|
+
/** Every run this daemon process has started, newest first — running runs
|
|
10634
|
+
* plus ones that settled (done/failed/cancelled) without a daemon
|
|
10635
|
+
* restart in between. Runs live in memory only for the process lifetime
|
|
10636
|
+
* (a restart loses this list, same as the rest of `runs` — the ATTESTATION
|
|
10637
|
+
* is what persists, in the ledger). Backs `review_ledger({includeRunning:
|
|
10638
|
+
* true})` and the `session_tree` review badge. */
|
|
10639
|
+
list(): ReviewRun[];
|
|
9267
10640
|
readonly ledger: ReviewLedger;
|
|
9268
10641
|
}
|
|
9269
10642
|
interface CreateReviewRunnerOptions {
|
|
@@ -9273,6 +10646,14 @@ interface CreateReviewRunnerOptions {
|
|
|
9273
10646
|
reviewers?: ReviewerSessionHost;
|
|
9274
10647
|
/** Attestor identity. Default: `agentproto-runtime@<hostname>`. */
|
|
9275
10648
|
daemonId?: string;
|
|
10649
|
+
/** Called once a run with a `requesterSessionId` reaches `done` — never
|
|
10650
|
+
* for `failed`/`cancelled` (no verdict to report). Display-only: the
|
|
10651
|
+
* daemon writes `text` into the requester's transcript as a `notice`
|
|
10652
|
+
* (see `sessions.ts`'s `recordNotice`) so `session_story`/`live_session`
|
|
10653
|
+
* show it — this never enqueues a prompt, touches the inbox, or starts a
|
|
10654
|
+
* turn. Fire-and-forget from the runner's perspective; a throw here is
|
|
10655
|
+
* swallowed. */
|
|
10656
|
+
notifyRequester?: (sessionId: string, text: string) => void;
|
|
9276
10657
|
}
|
|
9277
10658
|
/** Placeholder values the daemon binds for every review. `{changed}` is a
|
|
9278
10659
|
* shell-quoted turbo filter for the packages changed since the range base
|
|
@@ -9502,6 +10883,12 @@ interface RegisterReviewToolsOptions {
|
|
|
9502
10883
|
/** `gh` runner for `review_pr` — injectable for tests. Default: the real
|
|
9503
10884
|
* `gh` on the daemon's PATH. */
|
|
9504
10885
|
gh?: GhRunner;
|
|
10886
|
+
/** Resolve a session id to its subtree (itself + every session it
|
|
10887
|
+
* transitively spawned) — backs `review_ledger({requesterSessionId,
|
|
10888
|
+
* subtree: true})`. Omitted ⇒ `subtree` is ignored (scoped to the exact
|
|
10889
|
+
* id only), so a caller without sessions-registry access still works,
|
|
10890
|
+
* just without the subtree expansion. */
|
|
10891
|
+
resolveSubtree?: (sessionId: string) => readonly string[];
|
|
9505
10892
|
}
|
|
9506
10893
|
declare function registerReviewTools(server: McpServer, opts: RegisterReviewToolsOptions): void;
|
|
9507
10894
|
|
|
@@ -10295,6 +11682,108 @@ declare function formatToolCall(toolName: string, args: unknown): string;
|
|
|
10295
11682
|
*/
|
|
10296
11683
|
declare function formatToolResult(toolName: string | undefined, result: unknown, isError: boolean): string | null;
|
|
10297
11684
|
|
|
11685
|
+
/**
|
|
11686
|
+
* Pure sizing helper for a `tools/list` response — shared by
|
|
11687
|
+
* `scripts/measure-mcp-tools.mjs` (human-readable before/after report) and
|
|
11688
|
+
* the budget-guard test (`__tests__/tool-schema-budget.test.ts`) so both
|
|
11689
|
+
* measure the exact same thing: the JSON bytes an MCP client actually pays
|
|
11690
|
+
* for per tool (`description` + `inputSchema`), sorted biggest first.
|
|
11691
|
+
*/
|
|
11692
|
+
interface MeasuredToolEntry {
|
|
11693
|
+
name: string;
|
|
11694
|
+
bytes: number;
|
|
11695
|
+
}
|
|
11696
|
+
interface ToolListMeasurement {
|
|
11697
|
+
entries: MeasuredToolEntry[];
|
|
11698
|
+
totalBytes: number;
|
|
11699
|
+
}
|
|
11700
|
+
interface MeasurableTool {
|
|
11701
|
+
name: string;
|
|
11702
|
+
description?: string;
|
|
11703
|
+
inputSchema?: unknown;
|
|
11704
|
+
}
|
|
11705
|
+
/** UTF-8 byte size of the `{description, inputSchema}` JSON a client
|
|
11706
|
+
* receives for one tool — the same two fields `tools/list` sends over the
|
|
11707
|
+
* wire, so this tracks prompt-token cost rather than just character count. */
|
|
11708
|
+
declare function measureTool(tool: MeasurableTool): number;
|
|
11709
|
+
declare function measureToolList(tools: readonly MeasurableTool[]): ToolListMeasurement;
|
|
11710
|
+
|
|
11711
|
+
/**
|
|
11712
|
+
* Long-form MCP tool documentation, read from `docs/mcp-tools/<name>.md`.
|
|
11713
|
+
*
|
|
11714
|
+
* The schema `description` on a tool/field is a contract: what it does, and
|
|
11715
|
+
* the one rule that prevents a wrong call — kept short so `tools/list` stays
|
|
11716
|
+
* cheap. Everything else (edge cases, interactions with other fields,
|
|
11717
|
+
* examples, history) lives here instead, fetched on demand by the `tool_help`
|
|
11718
|
+
* MCP tool (`tool-help-mcp.ts`) and the `agentproto help <tool>` CLI verb
|
|
11719
|
+
* (`packages/cli/src/commands/help.ts`) — both call the two functions below,
|
|
11720
|
+
* so the markdown file is the single source both surfaces read.
|
|
11721
|
+
*
|
|
11722
|
+
* `docs/mcp-tools/` sits at the PACKAGE root (a sibling of `src`/`dist`, not
|
|
11723
|
+
* the repo-level `docs/`) so it ships inside the published npm tarball —
|
|
11724
|
+
* see the `files` array in `package.json`. Resolved relative to this
|
|
11725
|
+
* module's own directory rather than by package name: whether this code is
|
|
11726
|
+
* running from `src` (vitest, no build) or `dist` (built/published), the
|
|
11727
|
+
* module's directory is always a direct child of the package root, so one
|
|
11728
|
+
* `..` reaches it either way — no `require.resolve` needed.
|
|
11729
|
+
*/
|
|
11730
|
+
/** Full markdown help text for `name`, or `undefined` when no doc exists. */
|
|
11731
|
+
declare function getToolHelp(name: string): string | undefined;
|
|
11732
|
+
/** Every tool name with a help doc available — for a "did you mean" list. */
|
|
11733
|
+
declare function listToolHelpTopics(): string[];
|
|
11734
|
+
/** Extract a `## <topic>` section (case-insensitive) up to the next `##`
|
|
11735
|
+
* heading — shared by the `tool_help` MCP tool and the `agentproto help`
|
|
11736
|
+
* CLI verb so both slice a doc identically. `undefined` when no section
|
|
11737
|
+
* with that heading exists. */
|
|
11738
|
+
declare function extractHelpSection(doc: string, topic: string): string | undefined;
|
|
11739
|
+
|
|
11740
|
+
/**
|
|
11741
|
+
* Subtree usage rollup: one session's own usage plus the summed usage of
|
|
11742
|
+
* every session it (transitively) spawned — the shape `session_usage
|
|
11743
|
+
* { includeSubtree: true }` and `GET /sessions/:id/usage?includeSubtree=1`
|
|
11744
|
+
* return.
|
|
11745
|
+
*
|
|
11746
|
+
* Pure over a descriptor list (walked by `parentSessionId`, the same graph
|
|
11747
|
+
* `collectSubtree` uses for orchestrator scoping), so it's unit-testable
|
|
11748
|
+
* without a registry.
|
|
11749
|
+
*
|
|
11750
|
+
* Absent stays absent: a total is the sum of the sessions that REPORT the
|
|
11751
|
+
* field, and is omitted entirely when none of them do — a subtree of
|
|
11752
|
+
* cost-less sessions never reads as a measured $0.
|
|
11753
|
+
*/
|
|
11754
|
+
|
|
11755
|
+
/** Summed usage across the root and all its descendants. */
|
|
11756
|
+
interface SubtreeUsageTotals {
|
|
11757
|
+
/** Sessions counted, root included. */
|
|
11758
|
+
sessions: number;
|
|
11759
|
+
costUsd?: number;
|
|
11760
|
+
tokensIn?: number;
|
|
11761
|
+
tokensOut?: number;
|
|
11762
|
+
cacheReadTokens?: number;
|
|
11763
|
+
cacheWriteTokens?: number;
|
|
11764
|
+
}
|
|
11765
|
+
/** One direct child of the root, with that child's OWN usage (its own
|
|
11766
|
+
* descendants roll up into `subtree`, not here). */
|
|
11767
|
+
interface SubtreeChildUsage {
|
|
11768
|
+
id: string;
|
|
11769
|
+
label?: string;
|
|
11770
|
+
status: SessionDescriptor["status"];
|
|
11771
|
+
costUsd?: number;
|
|
11772
|
+
tokensIn?: number;
|
|
11773
|
+
tokensOut?: number;
|
|
11774
|
+
}
|
|
11775
|
+
interface SessionUsageWithSubtree {
|
|
11776
|
+
self: SessionUsage;
|
|
11777
|
+
subtree: SubtreeUsageTotals;
|
|
11778
|
+
children: SubtreeChildUsage[];
|
|
11779
|
+
}
|
|
11780
|
+
/**
|
|
11781
|
+
* Roll `root`'s usage up over its subtree. `all` should be the FULL
|
|
11782
|
+
* descriptor list (archived included) so an archived intermediate session
|
|
11783
|
+
* doesn't sever the parent→child chain.
|
|
11784
|
+
*/
|
|
11785
|
+
declare function rollupSessionSubtree(root: SessionDescriptor, all: readonly SessionDescriptor[]): SessionUsageWithSubtree;
|
|
11786
|
+
|
|
10298
11787
|
/**
|
|
10299
11788
|
* Dependency-injection surface for MCP credential resolution.
|
|
10300
11789
|
*
|
|
@@ -10399,6 +11888,63 @@ declare function enrichRollupWithProviderQuota(rollup: UsageRollup, window: stri
|
|
|
10399
11888
|
* such lines; skips any malformed or non-`usage_snapshot` line. Never throws. */
|
|
10400
11889
|
declare function readUsageSnapshots(sessionId: string, baseDir?: string): Promise<UsageSnapshotRecord[]>;
|
|
10401
11890
|
|
|
11891
|
+
/**
|
|
11892
|
+
* MCP surface for the sentinel primitive (AIP-60 §6): `sentinel_watch`,
|
|
11893
|
+
* `sentinel_list`, `sentinel_unwatch`, `sentinel_poll_now`.
|
|
11894
|
+
*
|
|
11895
|
+
* `sentinel_watch`'s `sessionId` defaults to the calling session — same
|
|
11896
|
+
* "trusted `?callerSessionId=`" pattern `message-tools.ts` uses — so a
|
|
11897
|
+
* session watching its own PR doesn't need to know its own id.
|
|
11898
|
+
*
|
|
11899
|
+
* Provider auto-select isn't implemented yet (design §6): `provider`
|
|
11900
|
+
* defaults to `local-gh`, the only built-in that ships as of this step.
|
|
11901
|
+
*
|
|
11902
|
+
* `createSentinelWatch`/`cancelSentinelWatch`/`sentinelView` are exported so
|
|
11903
|
+
* `http-server.ts`'s `POST/GET/DELETE /sentinels` routes (the daemon-HTTP
|
|
11904
|
+
* surface `agentproto sentinel` talks to) share the exact same
|
|
11905
|
+
* validation/sugar-resolution logic as this MCP surface — same pattern as
|
|
11906
|
+
* `TunnelRegistry` backing both `tunnel_create` and `POST /tunnels`, except
|
|
11907
|
+
* here the shared logic is a plain function rather than a stateful registry
|
|
11908
|
+
* method, since sentinel creation also needs the provider handle.
|
|
11909
|
+
*/
|
|
11910
|
+
|
|
11911
|
+
/** Compact wire view of a sentinel for tool/HTTP results — the internal-only
|
|
11912
|
+
* fields (`handle`, `seen`, `terminalSubjects`) never leave the daemon. */
|
|
11913
|
+
interface SentinelView {
|
|
11914
|
+
id: string;
|
|
11915
|
+
provider: string;
|
|
11916
|
+
status: SentinelStatus;
|
|
11917
|
+
match: SentinelMatchClause[];
|
|
11918
|
+
until: SentinelUntil;
|
|
11919
|
+
target: SentinelTarget;
|
|
11920
|
+
group?: string;
|
|
11921
|
+
label?: string;
|
|
11922
|
+
createdTs: number;
|
|
11923
|
+
eventCount: number;
|
|
11924
|
+
lastEventTs?: number;
|
|
11925
|
+
lastError?: string;
|
|
11926
|
+
}
|
|
11927
|
+
|
|
11928
|
+
/**
|
|
11929
|
+
* Smart default for `features.llmEndpoint` (daemon-managed-gateway): an
|
|
11930
|
+
* operator who already configured a named endpoint, an upstream credential
|
|
11931
|
+
* link, or an explicit `llm-endpoint` custom route is clearly USING the
|
|
11932
|
+
* proxy already — booting with the sidecar off (today's default) would
|
|
11933
|
+
* silently break their next `route:{gateway:"llm-endpoint"}` spawn on a
|
|
11934
|
+
* fresh install. An operator who never touched any of that gets the
|
|
11935
|
+
* unchanged, conservative default (off).
|
|
11936
|
+
*
|
|
11937
|
+
* An explicit `configured` value (`config.json`'s `features.llmEndpoint`,
|
|
11938
|
+
* profile-overlaid) always wins, in either direction — this resolver only
|
|
11939
|
+
* fills in the gap when the operator left the flag unset.
|
|
11940
|
+
*/
|
|
11941
|
+
/**
|
|
11942
|
+
* Resolve the EFFECTIVE `features.llmEndpoint` flag. `configured` is the
|
|
11943
|
+
* operator's merged config value (`undefined` when never set in either
|
|
11944
|
+
* `config.json` or the active profile).
|
|
11945
|
+
*/
|
|
11946
|
+
declare function resolveEffectiveLlmEndpointFlag(configured: boolean | undefined): Promise<boolean>;
|
|
11947
|
+
|
|
10402
11948
|
/**
|
|
10403
11949
|
* MCP tools that drive the pairing registry — the daemon's "let a client pair
|
|
10404
11950
|
* with me over an untrusted rendezvous" surface (design: DESIGN §6).
|
|
@@ -10420,6 +11966,102 @@ interface RegisterPairingToolsOptions {
|
|
|
10420
11966
|
}
|
|
10421
11967
|
declare function registerPairingTools(server: McpServer, opts: RegisterPairingToolsOptions): void;
|
|
10422
11968
|
|
|
11969
|
+
/**
|
|
11970
|
+
* Device registry — a read view over `PairingRegistry` (+, when wired,
|
|
11971
|
+
* `HostRegistry`) that presents each pairing/host as a "device" (DEVICES-PLAN
|
|
11972
|
+
* PR-A, hosts added PR-C). A paired CLIENT (today's `pair_offer`/`pair
|
|
11973
|
+
* accept`) gets `role: "client"`; a registered HOST (`pair offer --host` +
|
|
11974
|
+
* `devices add`, host-registry.ts) gets `role: "host"`.
|
|
11975
|
+
*
|
|
11976
|
+
* This is deliberately a thin computed view, not a second store: renaming
|
|
11977
|
+
* and revoking a client device mutate the same `pairings.json`
|
|
11978
|
+
* `PairingRegistry` already owns; a host device mutates the same
|
|
11979
|
+
* `hosts.json` `HostRegistry` — so `pair_*`/`/pairings` and
|
|
11980
|
+
* `device_*`/`/devices` are two surfaces over the same two sources of truth.
|
|
11981
|
+
*/
|
|
11982
|
+
|
|
11983
|
+
type DeviceRole = "client" | "host";
|
|
11984
|
+
type DeviceKind = "browser" | "cli" | "daemon";
|
|
11985
|
+
interface Device {
|
|
11986
|
+
fingerprint: string;
|
|
11987
|
+
name: string;
|
|
11988
|
+
role: DeviceRole;
|
|
11989
|
+
kind: DeviceKind;
|
|
11990
|
+
rendezvous: string;
|
|
11991
|
+
createdAt: string;
|
|
11992
|
+
lastSeen: string;
|
|
11993
|
+
/** A channel (offer or reconnect) is served for this device right now
|
|
11994
|
+
* (client) or a `forwardHttp` call is in flight for it right now (host).
|
|
11995
|
+
* For a host this is NOT a live heartbeat — see host-registry.ts's
|
|
11996
|
+
* "Online tracking" — it only reflects actual recent/current traffic. */
|
|
11997
|
+
online: boolean;
|
|
11998
|
+
/** A pair/v1 pairing: listed and revocable, but can't connect until
|
|
11999
|
+
* re-paired — see `PairingRecord.legacy`. */
|
|
12000
|
+
legacy?: true;
|
|
12001
|
+
/** Set when this device's pairing/host record was granted under a
|
|
12002
|
+
* HOST-scoped offer (`agentproto pair offer --host`) — see
|
|
12003
|
+
* offer-url.ts's "Offer scope". Surfaced from either side of a pairing so
|
|
12004
|
+
* a user can see, from EITHER daemon, which of their pairings/hosts grant
|
|
12005
|
+
* host control. */
|
|
12006
|
+
scope?: "host";
|
|
12007
|
+
/** Self-reported by a host at join time (SANDBOX-VISIBILITY-JOIN) — see
|
|
12008
|
+
* `HostRecord.provider`/`sandboxId`/`labels`. Absent for a client device. */
|
|
12009
|
+
provider?: string;
|
|
12010
|
+
sandboxId?: string;
|
|
12011
|
+
labels?: Record<string, string>;
|
|
12012
|
+
}
|
|
12013
|
+
interface DeviceRegistry {
|
|
12014
|
+
/** Every known device. Read-only. */
|
|
12015
|
+
list(): Promise<Device[]>;
|
|
12016
|
+
/** Rename a device (fingerprint or current name) to a new label. Tries the
|
|
12017
|
+
* pairing registry first, then the host registry. Returns false when
|
|
12018
|
+
* nothing matched. */
|
|
12019
|
+
rename(idOrName: string, newName: string): Promise<boolean>;
|
|
12020
|
+
/** Drop a device by fingerprint or name — same effect as `pair revoke` for
|
|
12021
|
+
* a client device, `devices revoke`-of-a-host for a host device. Tries
|
|
12022
|
+
* the pairing registry first, then the host registry. Returns false when
|
|
12023
|
+
* nothing matched. */
|
|
12024
|
+
revoke(idOrName: string): Promise<boolean>;
|
|
12025
|
+
/** Register a host from an offer URL (delegates to `HostRegistry.add`).
|
|
12026
|
+
* Rejects if no `HostRegistry` was wired into this device registry. */
|
|
12027
|
+
add(offerUrl: string, name?: string): Promise<{
|
|
12028
|
+
fingerprint: string;
|
|
12029
|
+
name: string;
|
|
12030
|
+
rendezvousUrl: string;
|
|
12031
|
+
}>;
|
|
12032
|
+
/** Forward one HTTP request to a HOST device on demand (delegates to
|
|
12033
|
+
* `HostRegistry.forwardHttp`) — the basis for `device_sessions`. Rejects
|
|
12034
|
+
* if no `HostRegistry` was wired, or if `idOrName` doesn't match a host. */
|
|
12035
|
+
forwardHttp(idOrName: string, req: ForwardHttpRequest): Promise<ForwardHttpResponse>;
|
|
12036
|
+
}
|
|
12037
|
+
declare function createDeviceRegistry(pairing: PairingRegistry, hosts?: HostRegistry): DeviceRegistry;
|
|
12038
|
+
|
|
12039
|
+
/**
|
|
12040
|
+
* MCP tools over the device registry (DEVICES-PLAN PR-A/PR-C) — `device_list`
|
|
12041
|
+
* / `device_rename` / `device_revoke` / `device_add`. A view on top of the
|
|
12042
|
+
* same registries `pair_offer`/`pair_list`/`pair_revoke` (pairing-tools.ts)
|
|
12043
|
+
* and host-registry.ts drive; those keep working unchanged. `device_revoke`
|
|
12044
|
+
* has the exact effect of `pair_revoke` for a client device.
|
|
12045
|
+
*
|
|
12046
|
+
* `device_add` (PR-C) registers a HOST from a `--host`-scoped offer URL —
|
|
12047
|
+
* see `HostRegistry.add`'s scope gate (host-registry.ts). It requires the
|
|
12048
|
+
* gateway to have wired a `HostRegistry` into the `DeviceRegistry`; without
|
|
12049
|
+
* one the call rejects with a clear message rather than a missing-tool error
|
|
12050
|
+
* (this repo always wires one when `pairingRegistry` is wired, so that's
|
|
12051
|
+
* only reachable via a deliberate host misconfiguration).
|
|
12052
|
+
*
|
|
12053
|
+
* Registered beside `pair_*` — same closure-rebind pattern; the registry
|
|
12054
|
+
* singleton lives on the gateway.
|
|
12055
|
+
*/
|
|
12056
|
+
|
|
12057
|
+
interface RegisterDeviceToolsOptions {
|
|
12058
|
+
registry: DeviceRegistry;
|
|
12059
|
+
/** Optional (SANDBOX-VISIBILITY-JOIN) — when wired, registers
|
|
12060
|
+
* `join_token_create`/`join_token_list`/`join_token_revoke`. */
|
|
12061
|
+
joinTokens?: JoinTokenRegistry;
|
|
12062
|
+
}
|
|
12063
|
+
declare function registerDeviceTools(server: McpServer, opts: RegisterDeviceToolsOptions): void;
|
|
12064
|
+
|
|
10423
12065
|
/**
|
|
10424
12066
|
* Rate-limited failure logging for reconnect loops.
|
|
10425
12067
|
*
|
|
@@ -11257,6 +12899,15 @@ interface SessionWaitResult {
|
|
|
11257
12899
|
* reported one. `"error"` means the adapter reported a failed turn.
|
|
11258
12900
|
* Same three-branch coverage as `empty`. */
|
|
11259
12901
|
reason?: string;
|
|
12902
|
+
/** The matched turn-end's captured error text — `SessionTurnEndEvent.error`
|
|
12903
|
+
* on the ring-replay/bus branches, `SessionDescriptor.lastTurnErrorMessage`
|
|
12904
|
+
* on the sync fast-path (no event object to read there). Present only
|
|
12905
|
+
* when `reason` is `"error"` AND the adapter emitted an in-band `error`
|
|
12906
|
+
* stream event during the turn. Same three-branch coverage as `empty`/
|
|
12907
|
+
* `reason` — this is the field a waiter (a supervisor session, `sessions
|
|
12908
|
+
* wait`, a webhook) needed to actually learn WHY a turn errored instead
|
|
12909
|
+
* of just that it did. */
|
|
12910
|
+
error?: string;
|
|
11260
12911
|
}
|
|
11261
12912
|
/**
|
|
11262
12913
|
* Block until one of the listed sessions fires a matching lifecycle event
|
|
@@ -11406,6 +13057,12 @@ interface CreateGatewayOptions {
|
|
|
11406
13057
|
* to an `enabled: false` summary. Also surfaced in `daemon_health` /
|
|
11407
13058
|
* `GET /health` so an operator can see the effective value. */
|
|
11408
13059
|
resumeSessionsOnBoot?: boolean;
|
|
13060
|
+
/** Opt-in continue-on-boot. Mirrors the `daemon.continueInterruptedOnBoot`
|
|
13061
|
+
* config knob; when true, the `continueInterruptedOnBoot()` handle method
|
|
13062
|
+
* runs the boot continue pass (see `continue-interrupted.ts`); when
|
|
13063
|
+
* false/omitted it short-circuits to an `enabled: false` summary. Surfaced
|
|
13064
|
+
* in `daemon_health` / `GET /health`. */
|
|
13065
|
+
continueInterruptedOnBoot?: boolean;
|
|
11409
13066
|
/** Idle agent-session reaper threshold in ms (PR-6). Mirrors the
|
|
11410
13067
|
* `daemon.idleReapAfterMs` config knob; the CLI resolves it (env >
|
|
11411
13068
|
* config > off) and passes it here. When positive, the gateway starts a
|
|
@@ -11659,11 +13316,38 @@ interface CreateGatewayOptions {
|
|
|
11659
13316
|
* returns) so the injected `serve` can capture the finished gateway.
|
|
11660
13317
|
*/
|
|
11661
13318
|
pairingRegistry?: PairingRegistry;
|
|
13319
|
+
/**
|
|
13320
|
+
* Optional HOST registry (see `createHostRegistry`, DEVICES-PLAN PR-C) —
|
|
13321
|
+
* the reverse-pairing "client" half of pair/v2, living daemon-side. When
|
|
13322
|
+
* wired alongside `pairingRegistry`, the gateway layers registered hosts
|
|
13323
|
+
* into `device_list`/`GET /devices`, and enables the `device_add` MCP tool
|
|
13324
|
+
* plus `POST /devices/add` + `POST /devices/:id/exec`. Unlike
|
|
13325
|
+
* `pairingRegistry`, this registry keeps no standing connections — every
|
|
13326
|
+
* `add`/`forwardHttp` dials fresh — so there is nothing for the gateway to
|
|
13327
|
+
* autoconnect or tear down on shutdown.
|
|
13328
|
+
*/
|
|
13329
|
+
hostRegistry?: HostRegistry;
|
|
13330
|
+
/**
|
|
13331
|
+
* Optional JOIN TOKEN registry (see `createJoinTokenRegistry`,
|
|
13332
|
+
* SANDBOX-VISIBILITY-JOIN). When wired alongside `pairingRegistry` +
|
|
13333
|
+
* `hostRegistry`, the gateway mounts `POST/GET /devices/join-tokens` +
|
|
13334
|
+
* `DELETE /devices/join-tokens/:id`, the `join_token_create/list/revoke`
|
|
13335
|
+
* MCP tools, and `device_sessions`/`GET /devices/:id/sessions[/…/output]`.
|
|
13336
|
+
* Autoconnect is the caller's to start (after this returns), same as
|
|
13337
|
+
* `pairingRegistry`.
|
|
13338
|
+
*/
|
|
13339
|
+
joinTokens?: JoinTokenRegistry;
|
|
11662
13340
|
/** Enable the local LLM Endpoint proxy sidecar (route registration,
|
|
11663
13341
|
* MCP tools, child-process lifecycle). Default false — the endpoint is
|
|
11664
13342
|
* an opt-in feature; when off, the `llm-endpoint` custom route is not
|
|
11665
13343
|
* registered and the `llm_endpoint_*` MCP tools are not exposed. */
|
|
11666
13344
|
llmEndpoint?: boolean;
|
|
13345
|
+
/** Mirrors `config.features.deviceInferenceShare` (DEVICES-PLAN item 1).
|
|
13346
|
+
* Default false. Only meaningful alongside `llmEndpoint: true` — passed
|
|
13347
|
+
* straight through to `startHttpServer`'s `deviceInferenceShare`, which
|
|
13348
|
+
* gates `/device-inference/*` (see http-server.ts's `HOST_SCOPE_HEADER`
|
|
13349
|
+
* doc comment for the full scope-gate story). */
|
|
13350
|
+
deviceInferenceShare?: boolean;
|
|
11667
13351
|
}
|
|
11668
13352
|
/**
|
|
11669
13353
|
* Default always-on set when `deferredTools` is enabled without an
|
|
@@ -11674,10 +13358,10 @@ interface CreateGatewayOptions {
|
|
|
11674
13358
|
* `toolPolicy.delegation: "deny"`, so those two names are simply absent
|
|
11675
13359
|
* from a deny-role's registry and cost nothing) but still needs to report
|
|
11676
13360
|
* back to its parent (`message_parent`), coordinate over the task ledger
|
|
11677
|
-
* (`task_*`), and observe its own session/turn state
|
|
11678
|
-
* (`session_context_status`
|
|
11679
|
-
* `session_monitor` / `session_events_poll`).
|
|
11680
|
-
* directory_*, command_*, remote_*, browser_*, terminal_*, mcp_*,
|
|
13361
|
+
* (`task_*`), and observe its own session/turn state and capabilities
|
|
13362
|
+
* (`session_context_status` / `session_capabilities` alongside the
|
|
13363
|
+
* pre-existing `session_list` / `session_monitor` / `session_events_poll`).
|
|
13364
|
+
* Everything else (fs_*, directory_*, command_*, remote_*, browser_*, terminal_*, mcp_*,
|
|
11681
13365
|
* routine_*, workflow_*, policy_*, inbound_watcher_*, session_tree,
|
|
11682
13366
|
* agent_export, adapter_list, every `app_ui_*` panel tool, ...) starts
|
|
11683
13367
|
* deferred — reachable via `tool_search`, never removed from `tools/call`.
|
|
@@ -11700,6 +13384,15 @@ interface GatewayHandle {
|
|
|
11700
13384
|
* `CreateGatewayOptions.pairingRegistry`. Undefined otherwise. Exposed so
|
|
11701
13385
|
* the CLI can `startAutoconnect()` after boot and `shutdown()` it. */
|
|
11702
13386
|
pairing?: PairingRegistry;
|
|
13387
|
+
/** HOST registry, when one was wired via `CreateGatewayOptions.hostRegistry`
|
|
13388
|
+
* (DEVICES-PLAN PR-C). Undefined otherwise. No autoconnect/shutdown to
|
|
13389
|
+
* call on it — see that option's doc comment. */
|
|
13390
|
+
hosts?: HostRegistry;
|
|
13391
|
+
/** JOIN TOKEN registry, when one was wired via
|
|
13392
|
+
* `CreateGatewayOptions.joinTokens` (SANDBOX-VISIBILITY-JOIN). Undefined
|
|
13393
|
+
* otherwise. Exposed so the CLI can `startAutoconnect()` after boot and
|
|
13394
|
+
* `shutdown()` it, same as `pairing`. */
|
|
13395
|
+
joinTokens?: JoinTokenRegistry;
|
|
11703
13396
|
/** Per-boot bearer token required on mutating /sessions/* routes
|
|
11704
13397
|
* + WS PTY upgrades. Exposed so an embedding host (e.g. the CLI
|
|
11705
13398
|
* shell that hosts the gateway in-process) can pass it to child
|
|
@@ -11724,6 +13417,16 @@ interface GatewayHandle {
|
|
|
11724
13417
|
resumeSessionsOnBoot(opts?: {
|
|
11725
13418
|
isServed?: (desc: SessionDescriptor) => boolean;
|
|
11726
13419
|
}): Promise<EagerResumeSummary>;
|
|
13420
|
+
/** Run the opt-in continue-on-boot pass and return its tally — a no-op
|
|
13421
|
+
* returning `{ enabled: false, ... }` when the `continueInterruptedOnBoot`
|
|
13422
|
+
* knob is off. serve.ts invokes this right AFTER `resumeSessionsOnBoot()`
|
|
13423
|
+
* (so a row whose eager resume failed is recognisable and skipped) — and
|
|
13424
|
+
* therefore, like it, after the supervisor was re-armed, so a re-armed
|
|
13425
|
+
* completion policy sees the continue turn's turn-end. Same `isServed`
|
|
13426
|
+
* cross-process gate. */
|
|
13427
|
+
continueInterruptedOnBoot(opts?: {
|
|
13428
|
+
isServed?: (desc: SessionDescriptor) => boolean;
|
|
13429
|
+
}): Promise<ContinueOnBootSummary>;
|
|
11727
13430
|
stop(): Promise<void>;
|
|
11728
13431
|
}
|
|
11729
13432
|
/**
|
|
@@ -11740,4 +13443,4 @@ interface GatewayHandle {
|
|
|
11740
13443
|
*/
|
|
11741
13444
|
declare function createGateway(opts: CreateGatewayOptions): Promise<GatewayHandle>;
|
|
11742
13445
|
|
|
11743
|
-
export { type ActivityKind, type ActivityListFilter, type ActivityPolicyLister, type ActivityProjector, type ActivityProjectorRegistry, type ActivityProjectorSession, type ActivityRecord, type ActivitySource, type ActivityState, type ActivityWaitingOn, type ActivityWorkflowLister, AdapterAuthDescriptor, type AdapterCapabilitiesLister, type AdapterInstallResult, type AdapterListEntry, type AgentAdapterInstaller, type AgentAdapterLister, type AgentAdapterResolver, type AgentSessionLike, type AgentStreamEvent, type AnthropicReaderConfig, AnthropicRemainingQuotaReader, type AttachPolicyInput, type AttachSandboxOpts, type AttachSandboxResult, AuthMethod, BUCKETS_ROOT, type BranchGcClass, type BranchGcCoverageView, type BranchGcHoldReason, type BranchGcKind, type BranchGcOutcomeView, type BranchGcPlanEntryView, type BranchGcPlanView, type BranchGcReclaimReason, type BranchGcResult, type BranchGcRunInput, type BranchGcRunner, type BranchGcStatus, type BranchGcSummaryView, type BranchGcTriageVerdict, type BranchGcVerdictInput, type BranchGcVerdictLookupInput, type BranchGcVerdictReader, type BranchGcVerdictRecordView, type BranchGcVerdictRecorder, type BrowserAdapterHandle, type BrowserAdapterInfo, type BrowserAdapterLister, type BrowserAdapterResolver, BuildHeartbeatAgent, CanonicalPosture, type CatalogModelsLister, CatalogModelsQuery, CatalogModelsResponse, type CatalogProviderModel, type CatalogProviderModelsQuery, type CatalogProviderModelsResponse, type CatalogProviderPricing, type ClaudeNativeRef, type CommitSpec, type CompletionPolicySupervisor, ContextProfile, type ConversationIndexRecord, type ConversationNativeRef, type CostBudgetDecision, type CrashDetectSummary, type CrashReaperRegistry, type CreateGatewayOptions, type CreateOfferInput, type CreateReviewRunnerOptions, type CreatedOffer, DEAD_SESSION_STATUSES, DEFAULT_ALWAYS_ON_TOOLS, DEFAULT_BUCKET, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_WORKTREE_ISOLATION, type DaemonReviewerHostDeps, DeclaredAdapterOption, type DeclaredAdapterPreset, DefaultsAdapterAuthConfig, type EagerResumeFailReason, type EagerResumeOutcome, type EagerResumeSkipReason, type EagerResumeSummary, EffortLevel, type EnableInput, type EnableResult, GC_REAPABLE_STATES, type GateSpec, type GatewayHandle, type GhRunner, type HarnessPreset, type HarnessPresetValidationDeps, HarnessPresetValidationError, type HarnessPresetsFile, type HermesNativeRef, INBOUND_PROVIDERS, type IdleReapSummary, type IdleReaperRegistry, type InboundEndpoint, type InboundEndpointStore, type InboundEndpointStoreOptions, type InboundEnqueuePrompt, type InboundIsSessionAlive, type InboundMessage, type InboundProvider, type InboundRestartSession, type InboundRouteMode, type InboundRouterDeps, type InboundRouterLog, type InboundSpawnForContact, type InboundWatcher, type JudgeGateSpec, LEGACY_SESSIONS_FILE, type LedgerAnnotations, type LedgerEntry, type LedgerHostMeta, type LocatedConversation, type LocatedConversationByPath, MAX_MESSAGE_DATA_BYTES, MCP_APP_BRIDGE_SCRIPT, MESSAGE_KINDS, MESSAGE_PREAMBLE, MESSAGE_TAG, MESSAGE_URGENCIES, type McpCredentialDeps, type MessageDeliveryVia, type MessageFilter, type MessageKind, type MessageRelation, type MessageTreeNode, type MessageUrgency, type MigrationMarker, type NormalizeInboundResult, type OnFailSpec, type OpenPrResolver, type OrchestratorGatewayDeps, type OrchestratorInjection, type OrchestratorInjector, type OrchestratorInjectorDeps, type OrchestratorMcpServerFactory, type OrchestratorScope, type OrphanReaperRegistry, PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, type PairingChannelContext, type PairingChannelHandle, type PairingChannelMode, type PairingRecord, type PairingRegistry, type PairingRegistryDeps, type PendingPermission, type PermissionRespondInput, type PermissionRespondResult, type PolicyRunState, type PolicyRunStatus, Posture, type PostureResolution, PrLookupError, type PrLookupErrorCode, type PrResolvedState, type PrStateResolver, type PrStatusSnapshot, type PresetInfo, type PricingResolver, type QuotaReadableProfile, type QuotaStoreFile, type QuotaStoreOptions, type ReconnectLogGate, type RegisterBrainToolsOptions, type RegisterBrowserInput, type RegisterPairingToolsOptions, type RegisterReviewToolsOptions, type RegisterSessionInput, type RegisterWebSearchToolsOptions, type RemainingQuota, type RemainingQuotaReader, type RemoteStatus, type ResolveNativeLinkInput, ResolvedAuthSpec, ResumeDisabledError, type ReuseResolution, type ReviewLedger, type ReviewLedgerFilter, type ReviewRun, type ReviewRunInput, type ReviewRunStatus, type ReviewRunner, type ReviewerRunResult, type ReviewerSessionHost, type RouteAwareLaunchConfig, type RouteAwareLaunchConfigInput, RouteSpec, type RuntimeMeta, SESSION_END_REASONS, SESSION_ID_ENV, type SandboxAdapterInfo, type SandboxConnectionDescriptor, type SandboxGcCandidate, type SandboxGcProviderHandle, type SandboxGcReapDeps, type SandboxGcReapResult, type SandboxLedgerEntry, type SandboxLedgerState, type SandboxProviderCapabilities, type SandboxProviderHandle, type SandboxProviderLister, type SandboxProviderResolver, type SandboxReconcileDeps, type SandboxReconcileProviderHandle, type SandboxReconcileResult, type SandboxReconcileRow, type SandboxReconcileVerdict, type ScopeTokenRegistry, SessionConfig, type SessionCurrentPhase, type SessionDescriptor, type SessionEndReason, type SessionKind, type SessionMessage, type SessionMessageFrom, type SessionMessageSentRecord, type SessionObserver, type SessionOutcome, type SessionOutcomeArtifact, type SessionOutcomeCompact, type SessionOutcomeLink, type SessionSnapshots, type SessionStatus, type SessionUsage, type SessionWaitEvent, type SessionWaitResult, type SessionsRegistry, type ShellGateSpec, type SpawnAgentInput, SpawnDefaultsConfig, type SpawnSessionInput, type StallWatchdogRegistry, type StallWatchdogSummary, type StoredProfileQuota, TASK_STATUSES, type TaskCaller, type TaskChangedEvent, type TaskClaimInput, type TaskCreateInput, type TaskGateOutcome, type TaskGateRunner, type TaskLedger, type TaskLedgerRegistry, type TaskLedgerSessionSlice, type TaskListFilter, type TaskRecord, type TaskStatus, type TaskUpdateInput, type TaskVerification, type TaskVerifySupervisor, type TaskWriteResult, type TokenPricing, type TransmitterBinding, type TransmitterBindingStore, type TunnelDescriptor, type TunnelProvider, type TunnelStatus, type UsageBucket, type UsageComputeInput, type UsageRollup, type UsageSnapshotRecord, type UsageSource, UserPreset, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, type WatcherDescriptor, type WatcherStartInput, type WebSearchInput, type WebSearchOutput, type WebSearchResult, type WorkspaceBrainSubscriber, type WorkspaceBrainSubscriberOptions, type WorkspaceBrains, WorkspaceFs, type WorktreeAutoReclaimer, type WorktreeDecision, type WorktreeField, type WorktreeGcClass, type WorktreeGcOutcomeView, type WorktreeGcPlanEntryView, type WorktreeGcReclaimReason, type WorktreeGcResult, type WorktreeGcRunInput, type WorktreeGcRunner, WorktreeIsolationMode, type WorktreeProvisionOutcome, type WorktreeProvisionRequest, type WorktreeProvisioner, type WorktreeRequest, type WorktreeStatusLister, type WorktreeStatusView, activityCounts, addHarnessPreset, appendConversationRecord, attachSandbox, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogProviderModels, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, canonicalForModeId, claudeProjectSlug, collectGcCandidates, compactOutcome, composeSessionObservers, conversationIndexPath, createActivityProjector, createDaemonReviewerHost, createFileStepCache, createGateway, createInboundEndpointStore, createOrchestratorInjector, createOrchestratorMcpServerFactory, createPairingRegistry, createPrProvenanceReconciler, createReconnectLogGate, createReviewLaneExecutor, createReviewLedger, createReviewRunner, createScopeTokenRegistry, createSessionMessage, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceBrainSubscriber, createWorkspaceBrains, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, defaultReviewLedgerRoot, defineBraveSearchHttpDriver, defineSerperHttpDriver, deriveSessionOutcome, deriveSessionUsage, enrichRollupWithProviderQuota, enrichWithRemainingQuota, escapeHumanPrompt, escapeMessageBody, evaluateCostBudget, execGh, fetchPrStatus, filterActivities, findConversationRecord, findNativeMode, findPrForCommit, formatToolCall, formatToolResult, getDefaultHarnessPreset, getHarnessPreset, getMcpCredentialDeps, githubRepoOf, harnessPresetsPath, hostPlaceholders, injectMcpAppBridge, isAgentCliAuthConfigured, isClosedTaskStatus, isKnownSessionEndReason, isMessageAllowed, isProviderLimitError, isSafeBucketSlug, isTerminalActivityState, listBuckets, listHarnessPresets, listPresets, loadHarnessPresets, loadQuotaStore, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, makeSandboxCredsStore, makeSandboxResolver, matchesMessageFilter, messageFrom, migrateLegacySessionsFile, migrationMarkerPath, mintMessageId, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeInbound, normalizeModeId, normalizeRemote, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parsePrUrl, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, policyToActivities, policyWatchesSession, prHeadSha, prToActivities, projectSessionUsage, readConversationIndex, readDaemonRegistry, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readSandboxLedger, readSessionForBrain, readUsageSnapshots, reapGcEntry, reapOrphanedDescendants, reconcileSandboxLedger, recordProfileQuota, recordSandboxBoot, recordSandboxLiveness, recordSandboxOrigin, recordSandboxState, registerBrainTools, registerBuiltinRoutes, registerPairingTools, registerReviewTools, registerSandboxAttachTool, registerWebSearchTools, removeHarnessPreset, removeSandboxLedgerEntry, renderInboxDigest, renderSessionMessage, renderSessionMessages, repoSlug, resolveBucketSlug, resolveNativeLink, resolvePosture, resolveRelation, resolveReuseFromLedger, resolveReviewerPreset, rollupUsage, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runShellLane, runStallWatchdogPass, sandboxLedgerPath, sanitizeLabel, setDefaultPreset, setMcpCredentialDeps, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, toWorktreeStatusView, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, updateHarnessPreset, upsertSandboxLedger, verifyInboundSignature, webSearchTool, withPr, withPrStatus, workflowToActivities, writeDaemonRegistryEntry };
|
|
13446
|
+
export { type ActivityKind, type ActivityListFilter, type ActivityPolicyLister, type ActivityProjector, type ActivityProjectorRegistry, type ActivityProjectorSession, type ActivityRecord, type ActivitySource, type ActivityState, type ActivityWaitingOn, type ActivityWorkflowLister, AdapterAuthDescriptor, type AdapterCapabilitiesLister, type AdapterInstallResult, type AdapterListEntry, type AgentAdapterInstaller, type AgentAdapterLister, type AgentAdapterResolver, type AgentSessionLike, type AgentStreamEvent, type AnthropicReaderConfig, AnthropicRemainingQuotaReader, type AttachPolicyInput, type AttachSandboxOpts, type AttachSandboxResult, AuthMethod, BUCKETS_ROOT, type BranchGcClass, type BranchGcCoverageView, type BranchGcHoldReason, type BranchGcKind, type BranchGcOutcomeView, type BranchGcPlanEntryView, type BranchGcPlanView, type BranchGcReclaimReason, type BranchGcResult, type BranchGcRunInput, type BranchGcRunner, type BranchGcStatus, type BranchGcSummaryView, type BranchGcTriageVerdict, type BranchGcVerdictInput, type BranchGcVerdictLookupInput, type BranchGcVerdictReader, type BranchGcVerdictRecordView, type BranchGcVerdictRecorder, type BrowserAdapterHandle, type BrowserAdapterInfo, type BrowserAdapterLister, type BrowserAdapterResolver, BuildHeartbeatAgent, CONTINUE_PROMPT_SOURCE, CanonicalPosture, type CapabilitiesInventory, type CapabilitiesInventoryMcp, type CapabilitiesInventorySkills, type CatalogModelsLister, CatalogModelsQuery, CatalogModelsResponse, type CatalogProviderModel, type CatalogProviderModelsQuery, type CatalogProviderModelsResponse, type CatalogProviderPricing, type ClaudeNativeRef, type CommitSpec, type CompletionPolicySupervisor, ContextProfile, type ContinueInterruptedMode, type ContinueInterruptedOutcome, type ContinueInterruptedResult, type ContinueInterruptedSkipReason, type ContinueOnBootSummary, type ConversationIndexRecord, type ConversationNativeRef, type CostBudgetDecision, type CrashDetectSummary, type CrashReaperRegistry, type CreateGatewayOptions, type CreateJoinTokenInput, type CreateOfferInput, type CreateReviewRunnerOptions, type CreatedJoinToken, type CreatedOffer, DEAD_SESSION_STATUSES, DEFAULT_ALWAYS_ON_TOOLS, DEFAULT_BUCKET, DEFAULT_CONTINUE_PROMPT, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_WORKTREE_ISOLATION, type DaemonReviewerHostDeps, DeclaredAdapterOption, type DeclaredAdapterPreset, DefaultsAdapterAuthConfig, type Device, type DeviceKind, type DeviceRegistry, type DeviceRole, type EagerResumeFailReason, type EagerResumeOutcome, type EagerResumeSkipReason, type EagerResumeSummary, EffortLevel, type EnableInput, type EnableResult, type ForwardHttpRequest, type ForwardHttpResponse, GC_REAPABLE_STATES, type GateSpec, type GatewayHandle, type GhRunner, HOSTS_VERSION, HOST_SCOPE_HEADER, type HarnessPreset, type HarnessPresetValidationDeps, HarnessPresetValidationError, type HarnessPresetsFile, type HermesNativeRef, type HostJoinMeta, type HostRecord, type HostRegistry, type HostRegistryDeps, INBOUND_PROVIDERS, type IdleReapSummary, type IdleReaperRegistry, type InboundEndpoint, type InboundEndpointStore, type InboundEndpointStoreOptions, type InboundEnqueuePrompt, type InboundIsSessionAlive, type InboundMessage, type InboundProvider, type InboundRestartSession, type InboundRouteMode, type InboundRouterDeps, type InboundRouterLog, type InboundSpawnForContact, type InboundWatcher, JOIN_TOKENS_VERSION, type JoinTokenRecord, type JoinTokenRegistry, type JoinTokenRegistryDeps, type JudgeGateSpec, LEGACY_SESSIONS_FILE, type LedgerAnnotations, type LedgerEntry, type LedgerHostMeta, type LlmEndpointDescriptor, type LlmEndpointStatus, type LlmEndpointStatusReport, type LocatedConversation, type LocatedConversationByPath, MAX_AUTO_CONTINUE_ATTEMPTS, MAX_MESSAGE_DATA_BYTES, MCP_APP_BRIDGE_SCRIPT, MESSAGE_KINDS, MESSAGE_PREAMBLE, MESSAGE_TAG, MESSAGE_URGENCIES, type McpCredentialDeps, type MeasurableTool, type MeasuredToolEntry, type MessageDeliveryVia, type MessageFilter, type MessageKind, type MessageRelation, type MessageTreeNode, type MessageUrgency, type MigrationMarker, type NormalizeInboundResult, type OnFailSpec, type OpenPrResolver, type OrchestratorGatewayDeps, type OrchestratorInjection, type OrchestratorInjector, type OrchestratorInjectorDeps, type OrchestratorMcpServerFactory, type OrchestratorScope, type OrphanReaperRegistry, PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, type PairingChannelContext, type PairingChannelHandle, type PairingChannelMode, type PairingRecord, type PairingRegistry, type PairingRegistryDeps, type PendingPermission, type PermissionRespondInput, type PermissionRespondResult, type PolicyRunState, type PolicyRunStatus, Posture, type PostureResolution, PrLookupError, type PrLookupErrorCode, type PrResolvedState, type PrStateResolver, type PrStatusSnapshot, type PresetInfo, type PricingResolver, type QuotaReadableProfile, type QuotaStoreFile, type QuotaStoreOptions, type ReconnectLogGate, type RegisterBrainToolsOptions, type RegisterBrowserInput, type RegisterDeviceToolsOptions, type RegisterPairingToolsOptions, type RegisterReviewToolsOptions, type RegisterSessionInput, type RegisterWebSearchToolsOptions, type RemainingQuota, type RemainingQuotaReader, type RemoteStatus, type ResolveNativeLinkInput, ResolvedAuthSpec, ResumeDisabledError, type ReuseResolution, type ReviewLedger, type ReviewLedgerFilter, type ReviewRun, type ReviewRunInput, type ReviewRunStatus, type ReviewRunner, type ReviewerRunResult, type ReviewerSessionHost, type RouteAwareLaunchConfig, type RouteAwareLaunchConfigInput, RouteSpec, type RuntimeMeta, SESSION_END_REASONS, SESSION_ID_ENV, type SandboxAdapterInfo, type SandboxConnectionDescriptor, type SandboxGcCandidate, type SandboxGcProviderHandle, type SandboxGcReapDeps, type SandboxGcReapResult, type SandboxLedgerEntry, type SandboxLedgerState, type SandboxProviderCapabilities, type SandboxProviderHandle, type SandboxProviderLister, type SandboxProviderResolver, type SandboxReconcileDeps, type SandboxReconcileProviderHandle, type SandboxReconcileResult, type SandboxReconcileRow, type SandboxReconcileVerdict, type ScopeTokenRegistry, type SentinelMatchClause, type SentinelSpec, type SentinelStatus, type SentinelTarget, type SentinelUntil, type SentinelView, SessionConfig, type SessionCurrentPhase, type SessionDescriptor, type SessionEndReason, type SessionKind, type SessionMessage, type SessionMessageFrom, type SessionMessageSentRecord, type SessionObserver, type SessionOutcome, type SessionOutcomeArtifact, type SessionOutcomeCompact, type SessionOutcomeLink, type SessionSnapshots, type SessionStatus, type SessionUsage, type SessionUsageWithSubtree, type SessionWaitEvent, type SessionWaitResult, type SessionsRegistry, type ShellGateSpec, type SkillPackInventoryEntry, type SkillsByHarnessEntry, type SpawnAgentInput, SpawnDefaultsConfig, type SpawnSessionInput, type StallWatchdogRegistry, type StallWatchdogSummary, type StoredProfileQuota, type SubtreeChildUsage, type SubtreeUsageTotals, TASK_STATUSES, type TaskCaller, type TaskChangedEvent, type TaskClaimInput, type TaskCreateInput, type TaskGateOutcome, type TaskGateRunner, type TaskLedger, type TaskLedgerRegistry, type TaskLedgerSessionSlice, type TaskListFilter, type TaskRecord, type TaskStatus, type TaskUpdateInput, type TaskVerification, type TaskVerifySupervisor, type TaskWriteResult, type TokenPricing, type ToolListMeasurement, type TransmitterBinding, type TransmitterBindingStore, type TunnelDescriptor, type TunnelProvider, type TunnelStatus, type UsageBucket, type UsageComputeInput, type UsageDetail, type UsageReadContext, type UsageReadResult, type UsageRollup, type UsageSnapshotRecord, type UsageSource, UserPreset, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, type WatcherDescriptor, type WatcherStartInput, type WebSearchInput, type WebSearchOutput, type WebSearchResult, type WorkspaceBrainSubscriber, type WorkspaceBrainSubscriberOptions, type WorkspaceBrains, WorkspaceFs, type WorktreeAutoReclaimer, type WorktreeDecision, type WorktreeField, type WorktreeGcClass, type WorktreeGcOutcomeView, type WorktreeGcPlanEntryView, type WorktreeGcReclaimReason, type WorktreeGcResult, type WorktreeGcRunInput, type WorktreeGcRunner, WorktreeIsolationMode, type WorktreeProvisionOutcome, type WorktreeProvisionRequest, type WorktreeProvisioner, type WorktreeRequest, type WorktreeStatusLister, type WorktreeStatusView, activityCounts, addHarnessPreset, appendConversationRecord, attachSandbox, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogProviderModels, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, canonicalForModeId, claudeProjectSlug, collectGcCandidates, compactOutcome, composeSessionObservers, continueInterruptedSessions, continueSkipReason, conversationIndexPath, createActivityProjector, createDaemonReviewerHost, createDeviceRegistry, createFileStepCache, createGateway, createHostRegistry, createInboundEndpointStore, createJoinTokenRegistry, createOrchestratorInjector, createOrchestratorMcpServerFactory, createPairingRegistry, createPrProvenanceReconciler, createReconnectLogGate, createReviewLaneExecutor, createReviewLedger, createReviewRunner, createScopeTokenRegistry, createSessionMessage, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceBrainSubscriber, createWorkspaceBrains, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, defaultReviewLedgerRoot, defineBraveSearchHttpDriver, defineSerperHttpDriver, deriveSessionOutcome, deriveSessionUsage, enrichRollupWithProviderQuota, enrichWithRemainingQuota, escapeHumanPrompt, escapeMessageBody, evaluateCostBudget, execGh, extractHelpSection, fetchPrStatus, filterActivities, findConversationRecord, findNativeMode, findPrForCommit, formatToolCall, formatToolResult, getDefaultHarnessPreset, getHarnessPreset, getMcpCredentialDeps, getToolHelp, githubRepoOf, harnessPresetsPath, hostPlaceholders, injectMcpAppBridge, isAgentCliAuthConfigured, isClosedTaskStatus, isKnownSessionEndReason, isMessageAllowed, isProviderLimitError, isSafeBucketSlug, isTerminalActivityState, listBuckets, listHarnessPresets, listPresets, listToolHelpTopics, loadHarnessPresets, loadQuotaStore, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, makeSandboxCredsStore, makeSandboxResolver, matchesMessageFilter, measureTool, measureToolList, messageFrom, migrateLegacySessionsFile, migrationMarkerPath, mintMessageId, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeInbound, normalizeModeId, normalizeRemote, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parsePrUrl, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, pickUsageDetail, policyToActivities, policyWatchesSession, prHeadSha, prToActivities, projectSessionUsage, readConversationIndex, readDaemonRegistry, readPairingsSnapshot, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readSandboxLedger, readSessionForBrain, readUsageSnapshots, reapGcEntry, reapOrphanedDescendants, reconcileSandboxLedger, recordProfileQuota, recordSandboxBoot, recordSandboxLiveness, recordSandboxOrigin, recordSandboxState, registerBrainTools, registerBuiltinRoutes, registerDeviceTools, registerPairingTools, registerReviewTools, registerSandboxAttachTool, registerWebSearchTools, removeHarnessPreset, removeSandboxLedgerEntry, renderInboxDigest, renderSessionMessage, renderSessionMessages, repoSlug, resolveBucketSlug, resolveEffectiveLlmEndpointFlag, resolveNativeLink, resolvePosture, resolveRelation, resolveReuseFromLedger, resolveReviewerPreset, rollupSessionSubtree, rollupUsage, runContinueOnBootPass, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runShellLane, runStallWatchdogPass, sandboxLedgerPath, sanitizeLabel, setDefaultPreset, setMcpCredentialDeps, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, toWorktreeStatusView, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, updateHarnessPreset, upsertSandboxLedger, verifyInboundSignature, webSearchTool, withPr, withPrStatus, workflowToActivities, writeDaemonRegistryEntry };
|