indusagi-coding-agent 0.2.2 → 0.2.4

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/entry.js +12434 -9445
  3. package/dist/guardrails.js +897 -37
  4. package/dist/index.js +14081 -11111
  5. package/dist/types/boot/heap.d.ts +31 -0
  6. package/dist/types/boot/runners/delegate-runner.d.ts +27 -1
  7. package/dist/types/boot/runners/server-mode.d.ts +71 -0
  8. package/dist/types/boot/runners/session.d.ts +26 -18
  9. package/dist/types/boot/runners/session.test.d.ts +6 -1
  10. package/dist/types/boot/server-token.d.ts +97 -0
  11. package/dist/types/capability-deck/cards/index.d.ts +1 -0
  12. package/dist/types/capability-deck/cards/task-card.d.ts +26 -2
  13. package/dist/types/capability-deck/cards/workflow-card.d.ts +55 -0
  14. package/dist/types/capability-deck/cards/workflow-card.test.d.ts +12 -0
  15. package/dist/types/capability-deck/contract.d.ts +7 -6
  16. package/dist/types/capability-deck/index.d.ts +1 -1
  17. package/dist/types/conductor/conductor.d.ts +24 -0
  18. package/dist/types/conductor/contract.d.ts +79 -7
  19. package/dist/types/conductor/index.d.ts +3 -3
  20. package/dist/types/conductor/permissions.d.ts +74 -4
  21. package/dist/types/conductor/post-edit-diagnostics.test.d.ts +8 -3
  22. package/dist/types/conductor/quota-error.d.ts +35 -0
  23. package/dist/types/conductor/signal-hub/translate.d.ts +4 -1
  24. package/dist/types/console/auth-status.d.ts +28 -0
  25. package/dist/types/console/components/AgentsView.d.ts +41 -0
  26. package/dist/types/console/components/BackgroundAgents.d.ts +63 -0
  27. package/dist/types/console/components/BackgroundAgents.test.d.ts +8 -0
  28. package/dist/types/console/components/Banner.d.ts +67 -33
  29. package/dist/types/console/components/TerminalConsole.d.ts +0 -5
  30. package/dist/types/console/components/welcome.d.ts +115 -0
  31. package/dist/types/console/components/welcome.test.d.ts +9 -0
  32. package/dist/types/console/contract.d.ts +16 -1
  33. package/dist/types/console/index.d.ts +3 -0
  34. package/dist/types/console/input/index.d.ts +1 -1
  35. package/dist/types/console/input/keymap.d.ts +2 -2
  36. package/dist/types/console/input/paste.d.ts +58 -6
  37. package/dist/types/console/overlays/approval-queue.d.ts +18 -1
  38. package/dist/types/console/overlays/boards.d.ts +55 -0
  39. package/dist/types/console/overlays/index.d.ts +1 -1
  40. package/dist/types/console/theme/adapter.d.ts +1 -1
  41. package/dist/types/console/theme/index.d.ts +1 -1
  42. package/dist/types/console/theme/palette.d.ts +10 -0
  43. package/dist/types/launch/login.d.ts +68 -0
  44. package/dist/types/launch/oauth.test.d.ts +20 -0
  45. package/dist/types/window-budget/summarize/condense.d.ts +6 -0
  46. package/dist/types/workflow-engine/agent-runner.d.ts +124 -0
  47. package/dist/types/workflow-engine/agent-runner.test.d.ts +8 -0
  48. package/dist/types/workflow-engine/display.d.ts +148 -0
  49. package/dist/types/workflow-engine/display.test.d.ts +1 -0
  50. package/dist/types/workflow-engine/engine.d.ts +183 -0
  51. package/dist/types/workflow-engine/engine.test.d.ts +1 -0
  52. package/dist/types/workflow-engine/index.d.ts +21 -0
  53. package/dist/types/workflow-engine/parse.d.ts +64 -0
  54. package/dist/types/workflow-engine/parse.test.d.ts +1 -0
  55. package/dist/types/workflow-engine/structured-output.d.ts +51 -0
  56. package/dist/types/workflow-engine/structured-output.test.d.ts +1 -0
  57. package/dist/types/workspace/brand.d.ts +1 -1
  58. package/package.json +2 -2
  59. package/dist/types/console/components/Emblem.d.ts +0 -49
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Heap headroom guard.
3
+ *
4
+ * Node's default old-space cap (~2 GB on 64-bit builds) is too small for heavy
5
+ * runs — most acutely a burst of parallel sub-agents whose combined transcripts
6
+ * push the resident heap past the default and abort the whole process with a V8
7
+ * "JavaScript heap out of memory" fatal (the exact crash this guards against).
8
+ *
9
+ * `--max-old-space-size` can only be set at process launch, so before any real
10
+ * work begins we re-exec the *same* program once with a raised cap, inherit its
11
+ * stdio (so the interactive TUI runs unchanged in the child), and adopt its exit
12
+ * code. The re-exec is one level deep: the child carries `INDUS_HEAP_BOOSTED=1`
13
+ * so it skips the guard and runs the agent directly.
14
+ *
15
+ * Controlled by env:
16
+ * INDUS_MAX_HEAP_MB target old-space size in MB (default 4096; 0 disables
17
+ * the guard entirely and runs in-process).
18
+ * INDUS_HEAP_BOOSTED set internally on the child; never set this yourself.
19
+ *
20
+ * If the re-exec cannot spawn (a locked-down sandbox, a missing argv[1]) the
21
+ * guard silently falls back to running in the current, un-boosted process rather
22
+ * than failing to start.
23
+ */
24
+ /**
25
+ * Ensure the process has heap headroom, re-execing once if it does not.
26
+ *
27
+ * Returns normally in three cases: the guard is disabled, the cap is already
28
+ * raised, or the re-exec could not be spawned. Otherwise it does NOT return —
29
+ * it runs the boosted child to completion and exits with the child's status.
30
+ */
31
+ export declare function ensureHeapHeadroom(): void;
@@ -25,7 +25,7 @@
25
25
  * The `spawn`/`tools` options are pure test seams — they let a unit test drive
26
26
  * the runner with an in-memory fake instead of a real network round-trip.
27
27
  */
28
- import { type AgentMessage } from "../../conductor";
28
+ import { type AgentMessage, type SessionPermissionPolicy } from "../../conductor";
29
29
  import { type AgentTool } from "../../capability-deck";
30
30
  import type { DelegateRunner } from "../../capability-deck/cards/task-card";
31
31
  /**
@@ -43,6 +43,12 @@ export interface DelegateSubAgent {
43
43
  messages: readonly AgentMessage[];
44
44
  error?: string;
45
45
  };
46
+ /**
47
+ * Optional event subscription (the real framework `Agent` provides it). The
48
+ * runner uses it to recompute live token spend as the sub-agent works; a test
49
+ * fake may omit it, in which case no progress is reported.
50
+ */
51
+ subscribe?(listener: () => void): () => void;
46
52
  }
47
53
  /** Configuration for {@link createDelegateRunner}. */
48
54
  export interface DelegateRunnerOptions {
@@ -69,6 +75,26 @@ export interface DelegateRunnerOptions {
69
75
  * `task` card — the recursion guard).
70
76
  */
71
77
  readonly tools?: () => AgentTool[];
78
+ /**
79
+ * Resolve the current server-tier gateway routing map (provider -> gateway
80
+ * base url). Called FRESH on every `run()` — never cached — so a mid-session
81
+ * `/login` (to "Indus Server"), including one that lands after this runner was
82
+ * constructed but before a given delegated call, is always picked up. This
83
+ * mirrors {@link getApiKey}, which is likewise invoked per-request rather than
84
+ * resolved once at construction.
85
+ */
86
+ readonly getGatewayBaseUrls?: () => Promise<Record<string, string>>;
87
+ /**
88
+ * The parent session's live permission policy: the SAME mutable rule list the
89
+ * parent gate reads plus a live getter onto the conductor's current mode. When
90
+ * present, every spawned sub-agent runs under a RESOLVER-LESS gate built from
91
+ * it — deny/ask rules, plan-mode enforcement, and the catastrophic-bash
92
+ * blocklist apply per inner tool call, and a mid-run mode switch (Shift+Tab)
93
+ * retargets the very next delegated tool call. A sub-agent cannot prompt, so
94
+ * an `ask` decision deterministically denies with an actionable message.
95
+ * Absent (a bare test construction), the sub-agent runs ungated as before.
96
+ */
97
+ readonly permissionPolicy?: SessionPermissionPolicy;
72
98
  }
73
99
  /**
74
100
  * Build a live {@link DelegateRunner} the host wires into the deck context.
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Boot helper: decide whether a provider runs in **server mode** (Case B).
3
+ *
4
+ * A model turn takes one of three paths (see LOGIN_GATEWAY_PLAN.md §1):
5
+ * - **A. Local key** — a usable key for the provider is stored in the on-disk
6
+ * auth vault (or exported in the environment). The framework calls the
7
+ * provider directly; behaviour is unchanged and this module returns
8
+ * `{ serverMode: false }`.
9
+ * - **B. Server tier** — the provider is gateway-eligible, the user has **no**
10
+ * usable local key, and they hold a **valid server token**. The session token
11
+ * is vended as the provider "key" and the model is re-routed through the indus
12
+ * gateway. This module returns `{ serverMode: true, token }`.
13
+ * - **C. Blocked** — no local key and no valid token: `{ serverMode: false }`
14
+ * (the resolver then falls through to the framework's own env lookup, which
15
+ * surfaces the usual "no API key" guidance pointing at `indus login`).
16
+ *
17
+ * The two consumers in {@link file://./session.ts} call this with the SAME
18
+ * `(ctx, provider, vault)` triple so the key-resolver fallback and the
19
+ * gateway-base-url computation always agree on whether server mode is active.
20
+ */
21
+ import type { BootContext } from "../contract";
22
+ import type { AuthVault } from "../../launch/contract";
23
+ /**
24
+ * The outcome of {@link resolveServerMode}: whether the provider should route
25
+ * through the gateway this run, and (when so) the session token to hand back as
26
+ * the provider api key.
27
+ */
28
+ export interface ServerModeResult {
29
+ /** `true` => bind the gateway base url and use {@link token} as the key. */
30
+ serverMode: boolean;
31
+ /** The session token (`access_token`) to vend as the key, when `serverMode`. */
32
+ token?: string;
33
+ }
34
+ /**
35
+ * Whether `provider` may be routed through the indus gateway. Excludes the
36
+ * non-portable providers (vertex / bedrock / codex) and the deferred opencode
37
+ * `anthropic-messages` api per the §B.4 caveat.
38
+ */
39
+ export declare function isGatewayEligibleProvider(provider: string): boolean;
40
+ /** The server-tier provider slugs (for default-model selection / listing). */
41
+ export declare function serverTierProviders(): readonly string[];
42
+ /**
43
+ * Decide whether `provider` runs in server mode for this session.
44
+ *
45
+ * Server mode is active when ALL hold:
46
+ * 1. {@link isGatewayEligibleProvider}(provider) — excludes vertex/bedrock/codex
47
+ * and the deferred opencode `anthropic-messages` api (§B.4);
48
+ * 2. no EXPLICIT local key — the vault holds no usable key for the provider
49
+ * (an ambient env var is intentionally ignored for server-tier providers);
50
+ * 3. {@link hasValidServerToken}() — a stored, non-expired server token exists.
51
+ *
52
+ * Returns `{ serverMode: false }` whenever any condition fails, so the
53
+ * local-key (Case A) path is preserved byte-for-byte: the resolver then returns
54
+ * `undefined` and no gateway base url is bound.
55
+ *
56
+ * @param _ctx the boot context (reserved for future per-run policy; the vault is
57
+ * passed explicitly so callers share one instance)
58
+ * @param provider the framework provider slug to evaluate
59
+ * @param vault the on-disk credential vault to probe for a local key
60
+ */
61
+ export declare function resolveServerMode(_ctx: BootContext, provider: string, vault: AuthVault): Promise<ServerModeResult>;
62
+ /**
63
+ * Build the per-provider gateway base-url map for THIS session: for every
64
+ * gateway-eligible provider currently in server mode (no explicit local key +
65
+ * valid token), map `provider -> ${INDUS_SERVER_URL}/gateway/${provider}`.
66
+ *
67
+ * The conductor consults this map at EVERY model bind — including a runtime
68
+ * `/model` switch — so selecting ANY server-tier model (not just the one the
69
+ * session launched on) routes through the gateway. Empty when nothing qualifies.
70
+ */
71
+ export declare function resolveServerGatewayUrls(ctx: BootContext, vault: AuthVault): Promise<Record<string, string>>;
@@ -8,9 +8,10 @@
8
8
  * {@link ModelMatcher}; the conductor itself is built lazily so no framework agent
9
9
  * is constructed until the first turn runs.
10
10
  */
11
- import { type AgentTool, type SessionConductor } from "../../conductor";
11
+ import { type AgentTool, type ApprovalResolver, type CanUseToolFn, type PermissionMode, type PermissionRule, type SessionConductor } from "../../conductor";
12
12
  import { type MemoryStore } from "../../capability-deck";
13
13
  import { type DelegateRunner } from "../../capability-deck/cards/task-card";
14
+ import type { WorkflowAgentRunner } from "../../workflow-engine";
14
15
  import { type CheckpointStore } from "./checkpoint";
15
16
  import { type ContextDoc, type SkillCard } from "../../briefing";
16
17
  import type { BootContext, Invocation } from "../contract";
@@ -33,23 +34,7 @@ export declare function resolveModelId(ctx: BootContext): string;
33
34
  * bad/unreadable skills dir never sinks the session.
34
35
  */
35
36
  export declare function gatherModelSkills(cwd: string): SkillCard[];
36
- /**
37
- * Select the tool deck for the run, honouring `--no-tools` (empty) and `--tools`
38
- * (allow-list). Tool ids are matched case-insensitively with `_`/`-` stripped, so
39
- * a `--tools web_fetch,todo_read` request lines up with the deck's `webfetch` /
40
- * `todoread` ids.
41
- */
42
- /**
43
- * @param runner an optional live sub-agent {@link DelegateRunner}; when present it
44
- * is injected under {@link DELEGATE_HANDLE_KEY} so the `task` card delegates for
45
- * real instead of returning its `STUB_NOTE`. Omitted by callers that only need
46
- * a deck (the card then degrades gracefully).
47
- * @param checkpoint an optional per-session {@link CheckpointStore}; when present
48
- * it is injected under {@link CHECKPOINT_HANDLE_KEY} so the framework's
49
- * write/edit tools snapshot a file's pre-mutation content (rewind, #24). Omitted
50
- * callers get no checkpointing (the tools no-op the snapshot).
51
- */
52
- export declare function selectTools(cwd: string, inv: Invocation, runner?: DelegateRunner, memoryStore?: MemoryStore, checkpoint?: CheckpointStore): AgentTool[];
37
+ export declare function selectTools(cwd: string, inv: Invocation, runner?: DelegateRunner, memoryStore?: MemoryStore, checkpoint?: CheckpointStore, workflowRunner?: WorkflowAgentRunner): AgentTool[];
53
38
  /**
54
39
  * Compose the run's system prompt: `--system` replaces the built-in briefing,
55
40
  * `--append-system` adds a trailing block, and both compose (override then
@@ -69,6 +54,29 @@ export declare function composeSystem(tools: AgentTool[], inv: Invocation, cwd:
69
54
  * @param cwd the run's working directory
70
55
  */
71
56
  export declare function sessionScopeDir(sessionsRoot: string, cwd: string): string;
57
+ /**
58
+ * Build the `canUseTool` gate factory for a boot run.
59
+ *
60
+ * The factory receives a getter onto the conductor's LIVE permission mode plus the
61
+ * conductor's stable approval delegate, and returns the gate that reads them. The
62
+ * delegate is threaded straight into `requestApproval`: on a NON-interactive boot
63
+ * (oneshot / link) no resolver is ever installed, so the delegate denies and an
64
+ * `ask` decision deterministically denies with an actionable message. The
65
+ * interactive (React) console installs its overlay resolver via the conductor's
66
+ * `setApprovalResolver` AFTER mount, at which point the SAME delegate begins
67
+ * routing `ask` decisions to the overlay — no rebuild, fully additive.
68
+ *
69
+ * `rules` is the session's LIVE mutable rule list: the gate re-scans it on every
70
+ * call, and an `allow-always` approval appends into it (via `appendAllowRule`
71
+ * below), so the approval is remembered for the remainder of the session — and
72
+ * ONLY the session; nothing is ever written back to settings. Every gate the
73
+ * factory mints (and every sub-agent gate built over the same array) sees the
74
+ * appended rules immediately.
75
+ *
76
+ * Exported for the boot tests; product callers go through
77
+ * {@link buildSessionConductor}.
78
+ */
79
+ export declare function buildPermissionGate(rules: PermissionRule[], mode: PermissionMode, tools: readonly AgentTool[], planReachable?: boolean): ((currentMode: () => PermissionMode, requestApproval?: ApprovalResolver) => CanUseToolFn) | undefined;
72
80
  export declare function buildSessionConductor(ctx: BootContext): Promise<SessionConductor>;
73
81
  /**
74
82
  * The prompts a oneshot run submits: the positional first prompt when present,
@@ -1,10 +1,15 @@
1
1
  /**
2
- * Skills-surface wiring (#7 / fixes #11).
2
+ * Skills-surface wiring (#7 / fixes #11) + the boot permission-gate factory.
3
3
  *
4
4
  * Proves the chain that makes on-disk `SKILL.md` cards visible to the model:
5
5
  * `gatherModelSkills` walks `cwd/.indusagi/skills`, drops any card flagged
6
6
  * `disable-model-invocation`, and the survivors render into the briefing's
7
7
  * `<available_skills>` block via `composeSystem`. A `--system` override must NOT
8
8
  * carry the skills block (it replaces the whole prompt).
9
+ *
10
+ * `buildPermissionGate` is pinned here too: default mode ALWAYS builds a gate
11
+ * (only a rule-less bypass boot with plan unreachable goes gate-free), and the
12
+ * factory wires `appendAllowRule` into the live rule list so an `allow-always`
13
+ * approval is remembered for the remainder of the session (B2).
9
14
  */
10
15
  export {};
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Server-token store — the boot-layer persistence for the indus device-login
3
+ * session token. This is the app side of the login + model-gateway contract: a
4
+ * successful `indus login` device flow writes the better-auth session token
5
+ * here, and the key resolver later reads it back to drive "server mode" (Case B
6
+ * — no local provider key, but a valid server session, so requests are routed
7
+ * through the indus-server gateway with the session token as the api key).
8
+ *
9
+ * The token lives in a single JSON file under the app's profile directory
10
+ * (`~/.indusagi/agent/server-token.json`) — deliberately NOT the framework's
11
+ * `~/.better-auth` location, so the CLI session is isolated from any other
12
+ * tooling. As with the credential vault, the file is tiny and access is
13
+ * interactive, so we read / write the whole file each time and tolerate a
14
+ * missing or malformed file as "no token".
15
+ *
16
+ * Ported from supercli `server/src/lib/token.ts`; the load-bearing detail is the
17
+ * `expires_in` (seconds, from the device-flow response) → `expires_at` (absolute
18
+ * ISO timestamp, on disk) conversion, which lets {@link isServerTokenExpired}
19
+ * decide freshness without re-deriving the clock offset.
20
+ */
21
+ /**
22
+ * Root of the indus-server. Used to build the gateway base URL and the
23
+ * device-login endpoints. Any trailing slash(es) are stripped so callers can
24
+ * append paths with a single separator.
25
+ */
26
+ export declare const INDUS_SERVER_URL: string;
27
+ /** The app profile directory that holds the server session token. */
28
+ export declare const CONFIG_DIR: string;
29
+ /** Absolute path of the JSON file the session token is persisted to. */
30
+ export declare const TOKEN_FILE: string;
31
+ /** On-disk token shape (what {@link storeServerToken} writes / {@link getServerToken} returns). */
32
+ export interface TokenData {
33
+ /** The better-auth session token, used as the gateway api key in server mode. */
34
+ access_token: string;
35
+ /** Optional refresh token, when the device flow returns one. */
36
+ refresh_token?: string;
37
+ /** Token scheme; defaults to `"Bearer"`. */
38
+ token_type: string;
39
+ /** Optional granted scope string. */
40
+ scope?: string;
41
+ /** Absolute expiry as an ISO string, or null when the token has no expiry. */
42
+ expires_at: string | null;
43
+ /** When this record was written, as an ISO string. */
44
+ created_at: string;
45
+ }
46
+ /** Device-flow response shape accepted by {@link storeServerToken} (`expires_in` → `expires_at`). */
47
+ export interface ServerTokenInput {
48
+ /** The better-auth session token. */
49
+ access_token: string;
50
+ /** Optional refresh token. */
51
+ refresh_token?: string;
52
+ /** Token scheme; defaults to `"Bearer"` when omitted. */
53
+ token_type?: string;
54
+ /** Optional granted scope string. */
55
+ scope?: string;
56
+ /** Lifetime in seconds, relative to now; converted to an absolute `expires_at`. */
57
+ expires_in?: number;
58
+ }
59
+ /**
60
+ * Read the stored session token, tolerating a missing or malformed file as
61
+ * "no token".
62
+ *
63
+ * @returns the parsed {@link TokenData}, or `null` when nothing is stored.
64
+ */
65
+ export declare function getServerToken(): Promise<TokenData | null>;
66
+ /**
67
+ * Persist a session token from a device-flow response, converting the relative
68
+ * `expires_in` (seconds) into an absolute `expires_at` ISO timestamp and
69
+ * defaulting `token_type` to `"Bearer"`. Creates the profile directory if
70
+ * needed and writes the file with owner-only permissions.
71
+ *
72
+ * @param token the device-flow token payload.
73
+ * @returns `true` on success, `false` if the write failed.
74
+ */
75
+ export declare function storeServerToken(token: ServerTokenInput): Promise<boolean>;
76
+ /**
77
+ * Remove the stored session token (used by `indus logout`). A missing file is
78
+ * treated as nothing-to-do.
79
+ *
80
+ * @returns `true` when a file was deleted, `false` when none existed / removal failed.
81
+ */
82
+ export declare function clearServerToken(): Promise<boolean>;
83
+ /**
84
+ * Decide whether a token is expired. A token counts as expired when it is
85
+ * absent, has no `expires_at`, or has under 5 minutes of life remaining (the
86
+ * margin keeps a long turn from racing the expiry boundary).
87
+ *
88
+ * @param token the token to inspect (or `null`).
89
+ * @returns `true` when the token is missing or stale.
90
+ */
91
+ export declare function isServerTokenExpired(token: TokenData | null): boolean;
92
+ /**
93
+ * Convenience predicate: there is a stored token and it is not expired.
94
+ *
95
+ * @returns `true` when a usable (fresh) session token is on disk.
96
+ */
97
+ export declare function hasValidServerToken(): Promise<boolean>;
@@ -21,6 +21,7 @@ import type { CapabilityCard } from "../contract";
21
21
  export { todoCard, buildTodoCapability, TodoLedger, type TodoItem, type TodoState, type TodoWeight, type TodoParamsType, type TodoDetails, } from "./todo-card";
22
22
  export { daemonCard, buildDaemonCapability, DaemonTable, type DaemonState, type DaemonParamsType, type DaemonDetails, } from "./bg-process-card";
23
23
  export { taskCard, buildTaskCapability, DELEGATE_HANDLE_KEY, type DelegateRunner, type DelegateRequest, type DelegateResult, type TaskParamsType, type TaskDetails, } from "./task-card";
24
+ export { workflowCard, buildWorkflowCapability, WORKFLOW_HANDLE_KEY, type WorkflowParamsType, type WorkflowDetails, } from "./workflow-card";
24
25
  export { saasCard, buildSaasCapability, SAAS_GATEWAY_KEY, type SaasGatewayPort, type RemoteToolSummary, type RemoteExecution, type SaasParamsType, type SaasDetails, } from "./saas-card";
25
26
  export { memoryCard, buildMemoryCapability, InMemoryStore, MEMORY_HANDLE_KEY, type MemoryStore, type MemoryParamsType, type MemoryDetails, } from "./memory-card";
26
27
  export { enterPlanModeCard, exitPlanModeCard, buildEnterPlanModeCapability, buildExitPlanModeCapability, PLAN_HANDLE_KEY, type PlanController, type EnterPlanParamsType, type EnterPlanDetails, type ExitPlanParamsType, type ExitPlanDetails, } from "./plan-tools";
@@ -37,6 +37,20 @@ export interface DelegateResult {
37
37
  /** The sub-agent's final report, surfaced to the parent agent verbatim. */
38
38
  readonly report: string;
39
39
  }
40
+ /** One line of a sub-agent's live activity stream (for the drill-in view). */
41
+ export interface ActivityLine {
42
+ /** What kind of step this is, used to colour it. */
43
+ readonly tone: "reason" | "tool" | "result";
44
+ /** The display text (already shortened to a single line). */
45
+ readonly text: string;
46
+ }
47
+ /** Live progress a running sub-agent streams back while it works. */
48
+ export interface DelegateProgress {
49
+ /** Cumulative tokens the sub-agent has spent so far (input + output). */
50
+ readonly tokens: number;
51
+ /** The sub-agent's recent activity (reasoning + tool steps), newest last. */
52
+ readonly activity: readonly ActivityLine[];
53
+ }
40
54
  /**
41
55
  * The contract a host's sub-agent runner must satisfy to be wired in.
42
56
  *
@@ -47,8 +61,14 @@ export interface DelegateResult {
47
61
  export interface DelegateRunner {
48
62
  /** Optional roster of named agent profiles, surfaced in the tool description. */
49
63
  listAgents?(): readonly string[];
50
- /** Run one delegated objective and resolve with the sub-agent's report. */
51
- run(request: DelegateRequest, signal?: AbortSignal): Promise<DelegateResult>;
64
+ /**
65
+ * Run one delegated objective and resolve with the sub-agent's report.
66
+ *
67
+ * When an `onProgress` callback is supplied the runner streams live metrics
68
+ * (token spend) as the sub-agent works, so the host can surface them (the
69
+ * background-agents panel). Optional — a runner may ignore it.
70
+ */
71
+ run(request: DelegateRequest, signal?: AbortSignal, onProgress?: (progress: DelegateProgress) => void): Promise<DelegateResult>;
52
72
  }
53
73
  declare const TaskParams: import("@sinclair/typebox").TObject<{
54
74
  objective: import("@sinclair/typebox").TString;
@@ -65,6 +85,10 @@ export interface TaskDetails {
65
85
  readonly ok: boolean;
66
86
  /** The agent profile that ran, if one was named. */
67
87
  readonly agent?: string;
88
+ /** Cumulative tokens the sub-agent spent — streamed live on progress updates. */
89
+ readonly tokens?: number;
90
+ /** The sub-agent's recent activity — streamed live for the drill-in view. */
91
+ readonly activity?: readonly ActivityLine[];
68
92
  }
69
93
  /**
70
94
  * Build the task/delegate capability.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Workflow capability — orchestrate a deterministic fan-out of subagents.
3
+ *
4
+ * App-novel wiring, sibling to (and UNRELATED to) the {@link "./task-card"}
5
+ * delegate card. The `workflow` tool runs a deterministic JavaScript workflow
6
+ * script that calls `agent()`, `parallel()`, and `pipeline()` to fan work out
7
+ * across many fresh subagents and fan their results back in, all under a live
8
+ * `◆ Workflow:` progress tree streamed into the tool result.
9
+ *
10
+ * The actual subagent runner is NOT owned by this card — it is a framework /
11
+ * boot concern injected through {@link DeckContext.framework} under the
12
+ * {@link WORKFLOW_HANDLE_KEY} key. When that handle is present the capability
13
+ * really runs the workflow (via the pure {@link runWorkflow} engine); when it is
14
+ * absent (tests, headless tooling, a host that has not wired the runner) the
15
+ * capability degrades gracefully to a clearly-typed {@link STUB_NOTE} result
16
+ * rather than throwing, exactly like the task card's `readDelegateRunner` path.
17
+ *
18
+ * Recursion guard: this card is registered all-profile-only in
19
+ * {@link "../cards/index"} (`APP_NOVEL_CARDS`), so the `'authoring'` deck a
20
+ * workflow subagent runs against EXCLUDES it — a subagent cannot call `workflow`.
21
+ *
22
+ * Live UI: each engine lifecycle callback mutates a {@link WorkflowSnapshot} and
23
+ * calls the framework `onUpdate` with the snapshot rendered through
24
+ * {@link renderWorkflowText} (the v1 string renderer). Updates are coalesced to
25
+ * status transitions (phase / agent start / agent end), not every log line, to
26
+ * avoid render thrash. The conductor re-projects the framework
27
+ * `tool_execution_update` event to a `tool_update` product signal (stage 3).
28
+ */
29
+ import { type Static } from "@sinclair/typebox";
30
+ import type { Capability, CapabilityCard, DeckContext } from "../contract";
31
+ import { type WorkflowSnapshot } from "../../workflow-engine";
32
+ /** Key under which a host wires a live workflow agent runner into the context. */
33
+ export declare const WORKFLOW_HANDLE_KEY: "workflow";
34
+ declare const WorkflowParams: import("@sinclair/typebox").TObject<{
35
+ script: import("@sinclair/typebox").TString;
36
+ args: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TAny>;
37
+ }>;
38
+ /** Statically-inferred parameter type the capability's `execute` receives. */
39
+ export type WorkflowParamsType = Static<typeof WorkflowParams>;
40
+ /** Structured detail returned alongside the model-facing content (the snapshot). */
41
+ export type WorkflowDetails = WorkflowSnapshot;
42
+ /**
43
+ * Build the workflow capability.
44
+ *
45
+ * If a {@link WorkflowAgentRunner} is present on the context it is bound and the
46
+ * tool truly orchestrates; otherwise the tool builds anyway and returns a typed,
47
+ * non-throwing stub so the deck stays assemblable in every environment.
48
+ *
49
+ * @param ctx the deck context; an optional runner is read from
50
+ * `ctx.framework[WORKFLOW_HANDLE_KEY]`.
51
+ */
52
+ export declare function buildWorkflowCapability(ctx: DeckContext): Capability<typeof WorkflowParams, WorkflowDetails>;
53
+ /** Catalog row for the workflow capability. */
54
+ export declare const workflowCard: CapabilityCard;
55
+ export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Workflow card — focused unit tests.
3
+ *
4
+ * The card is a pure capability builder over the pure {@link runWorkflow} engine
5
+ * (an injected {@link WorkflowAgentRunner} does the spawning). These tests cover:
6
+ * - the STUB_NOTE degrade when no runner handle is wired (no throw),
7
+ * - a real run with an in-memory fake runner: lifecycle → snapshot,
8
+ * - abort flips running agents to skipped,
9
+ * - and the deck-assembly guarantee: workflow appears in the `all` profile but
10
+ * NOT in the `authoring` subagent profile (the recursion guard).
11
+ */
12
+ export {};
@@ -124,12 +124,13 @@ export type CardProfiles = readonly DeckProfile[];
124
124
  * The named capability sets a session can be provisioned with — the profile
125
125
  * table that replaces a trio of near-identical build functions.
126
126
  *
127
- * - `authoring` — the full mutating set: read/write/edit/ls + search + shell +
128
- * web + checklist + delegate + background processes + SaaS actions. The
129
- * default for an interactive coding session.
130
- * - `survey` — observe-only: read/ls/search/web/checklist. No filesystem
131
- * mutation and no shell. Safe when the agent must not change the workspace.
132
- * - `all` — every registered capability, nothing withheld.
127
+ * - `authoring` — the READ-ONLY built-in subset: read/ls/search/web/
128
+ * checklist-read. No filesystem mutation, no shell, no app-novel cards —
129
+ * the deck sub-agents (task/workflow) are provisioned with (see
130
+ * {@link "./provision"}'s PROFILE_TABLE, the authoritative mapping).
131
+ * - `survey` — every built-in (mutating ones included), no app-novel cards.
132
+ * - `all` — every registered capability, nothing withheld. What an
133
+ * interactive coding session runs with.
133
134
  */
134
135
  export type DeckProfile = "authoring" | "survey" | "all";
135
136
  /**
@@ -31,7 +31,7 @@ export { CAPABILITY_CARDS, CAPABILITY_INDEX, CARD_PROFILES, capabilityIds, hasCa
31
31
  * (checklist, background-process proxy, delegate/sub-agent, SaaS connector,
32
32
  * working memory) plus their builders, stores, and injection-handle types.
33
33
  */
34
- export { APP_NOVEL_CARDS, todoCard, buildTodoCapability, TodoLedger, type TodoItem, type TodoState, type TodoWeight, type TodoParamsType, type TodoDetails, daemonCard, buildDaemonCapability, DaemonTable, type DaemonState, type DaemonParamsType, type DaemonDetails, taskCard, buildTaskCapability, DELEGATE_HANDLE_KEY, type DelegateRunner, type DelegateRequest, type DelegateResult, type TaskParamsType, type TaskDetails, saasCard, buildSaasCapability, SAAS_GATEWAY_KEY, type SaasGatewayPort, type RemoteToolSummary, type RemoteExecution, type SaasParamsType, type SaasDetails, memoryCard, buildMemoryCapability, InMemoryStore, MEMORY_HANDLE_KEY, type MemoryStore, type MemoryParamsType, type MemoryDetails, enterPlanModeCard, exitPlanModeCard, buildEnterPlanModeCapability, buildExitPlanModeCapability, PLAN_HANDLE_KEY, planSlug, planFilePath, writePlan, readPlan, PLANS_DIRNAME, type PlanController, type EnterPlanParamsType, type EnterPlanDetails, type ExitPlanParamsType, type ExitPlanDetails, } from "./cards/index";
34
+ export { APP_NOVEL_CARDS, todoCard, buildTodoCapability, TodoLedger, type TodoItem, type TodoState, type TodoWeight, type TodoParamsType, type TodoDetails, daemonCard, buildDaemonCapability, DaemonTable, type DaemonState, type DaemonParamsType, type DaemonDetails, taskCard, buildTaskCapability, DELEGATE_HANDLE_KEY, type DelegateRunner, type DelegateRequest, type DelegateResult, type TaskParamsType, type TaskDetails, workflowCard, buildWorkflowCapability, WORKFLOW_HANDLE_KEY, type WorkflowParamsType, type WorkflowDetails, saasCard, buildSaasCapability, SAAS_GATEWAY_KEY, type SaasGatewayPort, type RemoteToolSummary, type RemoteExecution, type SaasParamsType, type SaasDetails, memoryCard, buildMemoryCapability, InMemoryStore, MEMORY_HANDLE_KEY, type MemoryStore, type MemoryParamsType, type MemoryDetails, enterPlanModeCard, exitPlanModeCard, buildEnterPlanModeCapability, buildExitPlanModeCapability, PLAN_HANDLE_KEY, planSlug, planFilePath, writePlan, readPlan, PLANS_DIRNAME, type PlanController, type EnterPlanParamsType, type EnterPlanDetails, type ExitPlanParamsType, type ExitPlanDetails, } from "./cards/index";
35
35
  /**
36
36
  * Bridge ledger — event-sourced enrollment of dynamically grafted MCP tools:
37
37
  * content-hash / ULID key minting, the immutable {@link BridgeLedger} value with
@@ -89,6 +89,18 @@ export interface CondenseOpts {
89
89
  readonly force?: boolean;
90
90
  readonly model?: Model<any>;
91
91
  readonly contextTokens?: number;
92
+ /**
93
+ * Cancellation for a manual `/compact`. A hook MAY honor it mid-summarization;
94
+ * the conductor always checks it after the hook returns and discards the
95
+ * result when aborted, so even a hook that ignores the signal cancels cleanly.
96
+ */
97
+ readonly signal?: AbortSignal;
98
+ /**
99
+ * The bound model's credential, resolved through the session's per-call key
100
+ * resolver. Forwarded so the summarizer authenticates exactly like a turn —
101
+ * server-tier sessions have no env-var fallback for this.
102
+ */
103
+ readonly apiKey?: string;
92
104
  }
93
105
  /**
94
106
  * The pluggable condense hook. Given the active branch's messages (and an options
@@ -186,4 +198,16 @@ export declare function reduceState(prev: ConductorState, action: StateAction):
186
198
  * @param deps injectable collaborators; all optional, live defaults supplied
187
199
  */
188
200
  export declare function createSessionConductor(options: SessionConductorOptions, deps?: ConductorDeps): SessionConductor;
201
+ /**
202
+ * Return the framework model unchanged, or — when a gateway base URL is set
203
+ * ("server mode") — a CLONE with its `baseUrl` swapped to the gateway so the
204
+ * request flows through the quota-enforcing indus-server. Never mutates the
205
+ * shared catalog card (always spreads into a fresh object). When
206
+ * `gatewayBaseUrl` is `undefined` the model is returned by reference, keeping
207
+ * the local-key path byte-for-byte unchanged.
208
+ */
209
+ export declare function withGatewayBaseUrl<M extends {
210
+ baseUrl: string;
211
+ provider: string;
212
+ }>(model: M, gatewayBaseUrls: Record<string, string> | undefined): M;
189
213
  export {};