indusagi-coding-agent 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/entry.js +4 -4
  3. package/dist/index.js +4 -4
  4. package/package.json +3 -2
  5. package/src/_decls/entry.ts +18 -0
  6. package/src/_decls/guardrails.ts +35 -0
  7. package/src/_decls/index.ts +26 -0
  8. package/src/addons/contract.ts +236 -0
  9. package/src/addons/dispatch/event-dispatcher.ts +164 -0
  10. package/src/addons/dispatch/index.ts +25 -0
  11. package/src/addons/dispatch/tool-interceptor.ts +208 -0
  12. package/src/addons/host.ts +225 -0
  13. package/src/addons/index.ts +112 -0
  14. package/src/addons/manifest.ts +158 -0
  15. package/src/addons/sandbox.ts +170 -0
  16. package/src/addons/surface.ts +78 -0
  17. package/src/boot/auth-vault.ts +195 -0
  18. package/src/boot/boot.ts +138 -0
  19. package/src/boot/contract.ts +238 -0
  20. package/src/boot/heap.ts +59 -0
  21. package/src/boot/index.ts +28 -0
  22. package/src/boot/invocation.ts +93 -0
  23. package/src/boot/runners/addon-wiring.ts +153 -0
  24. package/src/boot/runners/checkpoint.ts +169 -0
  25. package/src/boot/runners/delegate-runner.ts +294 -0
  26. package/src/boot/runners/index.ts +13 -0
  27. package/src/boot/runners/link-runner.ts +45 -0
  28. package/src/boot/runners/memdir.ts +168 -0
  29. package/src/boot/runners/oneshot-runner.ts +58 -0
  30. package/src/boot/runners/read-state.ts +90 -0
  31. package/src/boot/runners/registry.ts +42 -0
  32. package/src/boot/runners/repl-runner.ts +143 -0
  33. package/src/boot/runners/server-mode.ts +121 -0
  34. package/src/boot/runners/session.ts +641 -0
  35. package/src/boot/server-token.ts +148 -0
  36. package/src/boot/stages.ts +167 -0
  37. package/src/boot/upgrade/apply.ts +94 -0
  38. package/src/boot/upgrade/index.ts +13 -0
  39. package/src/boot/upgrade/upgrades.ts +289 -0
  40. package/src/briefing/compose.ts +150 -0
  41. package/src/briefing/context-docs.ts +19 -0
  42. package/src/briefing/contract.ts +717 -0
  43. package/src/briefing/index.ts +31 -0
  44. package/src/briefing/macros.ts +97 -0
  45. package/src/briefing/skills.ts +47 -0
  46. package/src/capability-deck/bridge-ledger/index.ts +27 -0
  47. package/src/capability-deck/bridge-ledger/key.ts +67 -0
  48. package/src/capability-deck/bridge-ledger/ledger.ts +131 -0
  49. package/src/capability-deck/bridge-ledger/network.ts +117 -0
  50. package/src/capability-deck/builtin-bridge.ts +312 -0
  51. package/src/capability-deck/cards/bg-process-card.ts +335 -0
  52. package/src/capability-deck/cards/index.ts +115 -0
  53. package/src/capability-deck/cards/memory-card.ts +146 -0
  54. package/src/capability-deck/cards/plan-file.ts +97 -0
  55. package/src/capability-deck/cards/plan-tools.ts +185 -0
  56. package/src/capability-deck/cards/saas-card.ts +183 -0
  57. package/src/capability-deck/cards/task-card.ts +207 -0
  58. package/src/capability-deck/cards/todo-card.ts +168 -0
  59. package/src/capability-deck/cards/workflow-card.ts +247 -0
  60. package/src/capability-deck/contract.ts +388 -0
  61. package/src/capability-deck/index.ts +48 -0
  62. package/src/capability-deck/manifest.ts +109 -0
  63. package/src/capability-deck/provision.ts +169 -0
  64. package/src/channels/contract.ts +191 -0
  65. package/src/channels/framer.ts +50 -0
  66. package/src/channels/index.ts +101 -0
  67. package/src/channels/link/dialog.ts +129 -0
  68. package/src/channels/link/driver.ts +190 -0
  69. package/src/channels/link/index.ts +34 -0
  70. package/src/channels/link/server.ts +134 -0
  71. package/src/channels/oneshot.ts +90 -0
  72. package/src/channels/ops.ts +65 -0
  73. package/src/channels/session-ops.ts +81 -0
  74. package/src/conductor/bash-guard.ts +599 -0
  75. package/src/conductor/catalog/catalog.ts +116 -0
  76. package/src/conductor/catalog/index.ts +8 -0
  77. package/src/conductor/catalog/matcher.ts +134 -0
  78. package/src/conductor/conductor.ts +234 -0
  79. package/src/conductor/contract.ts +842 -0
  80. package/src/conductor/diagnostics.ts +227 -0
  81. package/src/conductor/index.ts +33 -0
  82. package/src/conductor/permissions.ts +588 -0
  83. package/src/conductor/quota-error.ts +49 -0
  84. package/src/conductor/signal-hub/hub.ts +46 -0
  85. package/src/conductor/signal-hub/index.ts +2 -0
  86. package/src/conductor/signal-hub/translate.test.ts +81 -0
  87. package/src/conductor/signal-hub/translate.ts +74 -0
  88. package/src/conductor/skill-parse/index.ts +2 -0
  89. package/src/conductor/skill-parse/parse.ts +108 -0
  90. package/src/conductor/transcript-store/index.ts +22 -0
  91. package/src/conductor/transcript-store/serialize.ts +116 -0
  92. package/src/conductor/transcript-store/store.ts +205 -0
  93. package/src/console/auth-status.ts +56 -0
  94. package/src/console/components/AgentsView.ts +165 -0
  95. package/src/console/components/BackgroundAgents.ts +155 -0
  96. package/src/console/components/Banner.ts +334 -0
  97. package/src/console/components/Composer.ts +94 -0
  98. package/src/console/components/StatusBar.ts +49 -0
  99. package/src/console/components/TerminalConsole.ts +1090 -0
  100. package/src/console/components/WorkingIndicator.ts +98 -0
  101. package/src/console/components/banner-sweep.ts +24 -0
  102. package/src/console/components/welcome.ts +74 -0
  103. package/src/console/contract.ts +630 -0
  104. package/src/console/index.ts +34 -0
  105. package/src/console/input/complete.ts +127 -0
  106. package/src/console/input/dir-reader.ts +34 -0
  107. package/src/console/input/index.ts +23 -0
  108. package/src/console/input/keymap.ts +159 -0
  109. package/src/console/input/paste.ts +104 -0
  110. package/src/console/mount.ts +56 -0
  111. package/src/console/overlays/approval-queue.ts +57 -0
  112. package/src/console/overlays/approval.ts +130 -0
  113. package/src/console/overlays/auth.ts +342 -0
  114. package/src/console/overlays/boards.ts +308 -0
  115. package/src/console/overlays/host.ts +36 -0
  116. package/src/console/overlays/index.ts +26 -0
  117. package/src/console/overlays/pickers.ts +258 -0
  118. package/src/console/overlays/sessions.ts +190 -0
  119. package/src/console/reducer.ts +182 -0
  120. package/src/console/slash/builtins.ts +81 -0
  121. package/src/console/slash/commands/dynamic.ts +83 -0
  122. package/src/console/slash/commands/integrations.ts +695 -0
  123. package/src/console/slash/commands/shared.ts +75 -0
  124. package/src/console/slash/commands/transcript.ts +263 -0
  125. package/src/console/slash/commands/workbench.ts +246 -0
  126. package/src/console/slash/index.ts +15 -0
  127. package/src/console/slash/registry.ts +70 -0
  128. package/src/console/slash/resolve.ts +63 -0
  129. package/src/console/startup.ts +209 -0
  130. package/src/console/theme/adapter.ts +45 -0
  131. package/src/console/theme/index.ts +7 -0
  132. package/src/console/theme/palette.ts +68 -0
  133. package/src/console/theme/resolve.ts +39 -0
  134. package/src/console/theme/tokens.ts +71 -0
  135. package/src/entry.ts +55 -0
  136. package/src/guardrails.ts +37 -0
  137. package/src/index.ts +18 -0
  138. package/src/insight/channel.ts +88 -0
  139. package/src/insight/contract.ts +185 -0
  140. package/src/insight/index.ts +110 -0
  141. package/src/insight/recorder.ts +213 -0
  142. package/src/insight/redaction.ts +157 -0
  143. package/src/insight/replay.ts +158 -0
  144. package/src/insight/sampling.ts +70 -0
  145. package/src/insight/serialize.ts +50 -0
  146. package/src/insight/sinks/console.ts +64 -0
  147. package/src/insight/sinks/file.ts +40 -0
  148. package/src/insight/sinks/index.ts +24 -0
  149. package/src/insight/sinks/stream.ts +54 -0
  150. package/src/integrations/sarvam/attach.ts +239 -0
  151. package/src/integrations/sarvam/config.ts +156 -0
  152. package/src/integrations/sarvam/index.ts +25 -0
  153. package/src/integrations/sarvam/sarvam.test.ts +60 -0
  154. package/src/integrations/sarvam/types.ts +27 -0
  155. package/src/integrations/zoho/attach.ts +342 -0
  156. package/src/integrations/zoho/config.ts +125 -0
  157. package/src/integrations/zoho/index.ts +27 -0
  158. package/src/integrations/zoho/types.ts +21 -0
  159. package/src/integrations/zoho/zoho.test.ts +50 -0
  160. package/src/kit/clipboard-image.ts +107 -0
  161. package/src/kit/external-editor.ts +48 -0
  162. package/src/kit/image.ts +59 -0
  163. package/src/kit/index.ts +51 -0
  164. package/src/kit/shell.ts +19 -0
  165. package/src/kit/tool-fetch.ts +85 -0
  166. package/src/launch/catalog.ts +148 -0
  167. package/src/launch/contract.ts +187 -0
  168. package/src/launch/credentials.ts +625 -0
  169. package/src/launch/index.ts +98 -0
  170. package/src/launch/invocation/attachments.ts +179 -0
  171. package/src/launch/invocation/flags.ts +196 -0
  172. package/src/launch/invocation/index.ts +25 -0
  173. package/src/launch/invocation/read.ts +260 -0
  174. package/src/launch/invocation/usage.ts +67 -0
  175. package/src/launch/login.ts +324 -0
  176. package/src/launch/oauth.test.ts +18 -0
  177. package/src/launch/oauth.ts +203 -0
  178. package/src/launch/packages.ts +194 -0
  179. package/src/launch/pickers.ts +189 -0
  180. package/src/runtime-bridge/bridges/_drive.ts +96 -0
  181. package/src/runtime-bridge/bridges/builtins.ts +68 -0
  182. package/src/runtime-bridge/bridges/claude-cli.ts +123 -0
  183. package/src/runtime-bridge/bridges/codex-cli.ts +142 -0
  184. package/src/runtime-bridge/bridges/index.ts +33 -0
  185. package/src/runtime-bridge/bridges/indusagi-cli.ts +155 -0
  186. package/src/runtime-bridge/broker.ts +227 -0
  187. package/src/runtime-bridge/contract.ts +122 -0
  188. package/src/runtime-bridge/index.ts +79 -0
  189. package/src/runtime-bridge/sink.ts +180 -0
  190. package/src/sessions/contract.ts +81 -0
  191. package/src/sessions/index.ts +13 -0
  192. package/src/sessions/library.ts +229 -0
  193. package/src/settings/contract.ts +114 -0
  194. package/src/settings/index.ts +32 -0
  195. package/src/settings/manager.ts +117 -0
  196. package/src/transcript-export/index.ts +45 -0
  197. package/src/transcript-export/publish.ts +260 -0
  198. package/src/transcript-export/sgr.ts +315 -0
  199. package/src/transcript-export/template.ts +272 -0
  200. package/src/transcript-export/theme-bridge.ts +150 -0
  201. package/src/window-budget/budget/estimate.ts +135 -0
  202. package/src/window-budget/budget/gate.ts +33 -0
  203. package/src/window-budget/budget/index.ts +16 -0
  204. package/src/window-budget/budget/slice.ts +56 -0
  205. package/src/window-budget/condenser.ts +58 -0
  206. package/src/window-budget/contract.ts +184 -0
  207. package/src/window-budget/index.ts +19 -0
  208. package/src/window-budget/microcompact.ts +95 -0
  209. package/src/window-budget/rehydrate.ts +136 -0
  210. package/src/window-budget/summarize/condense.ts +103 -0
  211. package/src/window-budget/summarize/index.ts +14 -0
  212. package/src/window-budget/summarize/prompt.ts +149 -0
  213. package/src/workflow-engine/agent-runner.ts +181 -0
  214. package/src/workflow-engine/display.ts +224 -0
  215. package/src/workflow-engine/engine.ts +294 -0
  216. package/src/workflow-engine/index.ts +23 -0
  217. package/src/workflow-engine/parse.ts +172 -0
  218. package/src/workflow-engine/structured-output.ts +35 -0
  219. package/src/workspace/brand.ts +29 -0
  220. package/src/workspace/index.ts +18 -0
  221. package/src/workspace/locator.ts +103 -0
  222. package/src/workspace/runtime-detect.ts +64 -0
@@ -0,0 +1,183 @@
1
+ /**
2
+ * SaaS-action capability — a thin wrapper over the framework's connector gateway
3
+ * (Composio-style remote tool execution).
4
+ *
5
+ * The heavy lifting (authenticating to a vendor, resolving toolkit scopes,
6
+ * executing a remote tool slug) lives in the framework's `connectors-saas`
7
+ * layer. This card does NOT re-implement any of it; it adapts an injected
8
+ * gateway handle to the deck's {@link Capability} shape so the agent can invoke
9
+ * remote SaaS actions by slug. When no gateway is wired into the context the card
10
+ * still builds and returns a typed stub, so the deck assembles in every
11
+ * environment.
12
+ *
13
+ * The single tool keys behavior on an `action` discriminant:
14
+ * - `discover` — list executable remote tool slugs (optionally filtered).
15
+ * - `execute` — run one remote tool slug with arguments.
16
+ *
17
+ * Vendor-defined identifiers (uppercase tool slugs like `GITHUB_CREATE_ISSUE`)
18
+ * are passed through verbatim — they are part of the connector's wire contract.
19
+ */
20
+ import { Type, type Static } from "@sinclair/typebox";
21
+ import { capabilityId, type Capability, type CapabilityCard, type DeckContext } from "../contract.js";
22
+
23
+ /** Key under which a host wires a live SaaS gateway into the deck context. */
24
+ export const SAAS_GATEWAY_KEY = "saasGateway" as const;
25
+
26
+ /** One executable remote tool advertised by the connector. */
27
+ export interface RemoteToolSummary {
28
+ /** Vendor-defined slug, passed through verbatim (e.g. `GITHUB_CREATE_ISSUE`). */
29
+ readonly slug: string;
30
+ /** Human-readable description from the connector, if any. */
31
+ readonly description?: string;
32
+ }
33
+
34
+ /** The outcome of executing one remote tool slug. */
35
+ export interface RemoteExecution {
36
+ readonly ok: boolean;
37
+ /** Connector payload, surfaced to the model as serialized text. */
38
+ readonly data: unknown;
39
+ }
40
+
41
+ /**
42
+ * The slice of a framework SaaS gateway this card adapts.
43
+ *
44
+ * Deliberately narrow — the deck only needs to list and execute remote tools.
45
+ * The framework's full gateway (connection lifecycle, scope planning, status
46
+ * reporting) is a superset; a host adapts it to this shape when wiring it in.
47
+ */
48
+ export interface SaasGatewayPort {
49
+ /** List executable remote tools, optionally filtered by toolkit or query. */
50
+ discover(filter?: {
51
+ toolkits?: string[];
52
+ query?: string;
53
+ }): Promise<RemoteToolSummary[]>;
54
+ /** Execute one remote tool slug with arguments. */
55
+ execute(slug: string, args: Record<string, unknown>): Promise<RemoteExecution>;
56
+ }
57
+
58
+ const SaasParams = Type.Object(
59
+ {
60
+ action: Type.Union([Type.Literal("discover"), Type.Literal("execute")], {
61
+ description:
62
+ "`discover` lists available remote tool slugs; `execute` runs one slug with arguments.",
63
+ }),
64
+ slug: Type.Optional(
65
+ Type.String({
66
+ description:
67
+ "Vendor tool slug to run (e.g. GITHUB_CREATE_ISSUE). Required for `execute`.",
68
+ }),
69
+ ),
70
+ arguments: Type.Optional(
71
+ Type.Record(Type.String(), Type.Unknown(), {
72
+ description: "Arguments object for the remote tool, as the connector expects.",
73
+ }),
74
+ ),
75
+ toolkits: Type.Optional(
76
+ Type.Array(Type.String(), {
77
+ description: "On `discover`, restrict results to these toolkit names.",
78
+ }),
79
+ ),
80
+ query: Type.Optional(
81
+ Type.String({
82
+ description: "On `discover`, free-text filter over available tools.",
83
+ }),
84
+ ),
85
+ },
86
+ { additionalProperties: false },
87
+ );
88
+
89
+ /** Statically-inferred parameter type the capability's `execute` receives. */
90
+ export type SaasParamsType = Static<typeof SaasParams>;
91
+
92
+ /** Structured detail returned alongside the model-facing content. */
93
+ export interface SaasDetails {
94
+ readonly action: "discover" | "execute";
95
+ readonly ok: boolean;
96
+ readonly slug?: string;
97
+ readonly count?: number;
98
+ }
99
+
100
+ const SAAS_DESCRIPTION =
101
+ 'Discover and run third-party SaaS actions (GitHub, Slack, Gmail, etc.) through the connector gateway. Use `action:"discover"` to find the right tool slug, then `action:"execute"` with that `slug` and an `arguments` object to perform the action. Tool slugs are vendor-defined and case-sensitive; pass them exactly as listed.';
102
+
103
+ const STUB_NOTE =
104
+ "The SaaS connector gateway is not configured in this environment, so no remote action was performed. Configure a connector (and authenticate the relevant toolkit) to enable these actions.";
105
+
106
+ /** Read the optional SaaS gateway handle from the deck context. */
107
+ function readGateway(ctx: DeckContext): SaasGatewayPort | undefined {
108
+ const handle = ctx.framework?.[SAAS_GATEWAY_KEY];
109
+ if (
110
+ handle &&
111
+ typeof (handle as SaasGatewayPort).discover === "function" &&
112
+ typeof (handle as SaasGatewayPort).execute === "function"
113
+ ) {
114
+ return handle as SaasGatewayPort;
115
+ }
116
+ return undefined;
117
+ }
118
+
119
+ /**
120
+ * Build the SaaS-action capability, adapting an injected gateway when present.
121
+ *
122
+ * @param ctx the deck context; an optional gateway is read from
123
+ * `ctx.framework[SAAS_GATEWAY_KEY]`.
124
+ */
125
+ export function buildSaasCapability(ctx: DeckContext): Capability<typeof SaasParams, SaasDetails> {
126
+ const gateway = readGateway(ctx);
127
+ return {
128
+ name: "saas-action",
129
+ label: "SaaS action",
130
+ description: SAAS_DESCRIPTION,
131
+ parameters: SaasParams,
132
+ async execute(_toolCallId, params) {
133
+ if (!gateway) {
134
+ return {
135
+ content: [{ type: "text", text: STUB_NOTE }],
136
+ details: { action: params.action, ok: false },
137
+ isError: true,
138
+ };
139
+ }
140
+ if (params.action === "discover") {
141
+ const tools = await gateway.discover({
142
+ toolkits: params.toolkits,
143
+ query: params.query,
144
+ });
145
+ const text =
146
+ tools.length === 0
147
+ ? "No remote tools matched."
148
+ : tools
149
+ .map((t) =>
150
+ t.description ? `- ${t.slug} \u2014 ${t.description}` : `- ${t.slug}`,
151
+ )
152
+ .join("\n");
153
+ return {
154
+ content: [{ type: "text", text }],
155
+ details: { action: "discover", ok: true, count: tools.length },
156
+ };
157
+ }
158
+ if (!params.slug) {
159
+ return {
160
+ content: [{ type: "text", text: "`slug` is required to execute a SaaS action." }],
161
+ details: { action: "execute", ok: false },
162
+ isError: true,
163
+ };
164
+ }
165
+ const result = await gateway.execute(params.slug, params.arguments ?? {});
166
+ return {
167
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
168
+ details: { action: "execute", ok: result.ok, slug: params.slug },
169
+ isError: result.ok ? undefined : true,
170
+ };
171
+ },
172
+ };
173
+ }
174
+
175
+ /** Catalog row for the SaaS-action capability. */
176
+ export const saasCard: CapabilityCard = {
177
+ id: capabilityId("saas-action"),
178
+ title: "SaaS action",
179
+ summary: "Discover and run third-party SaaS actions through the connector gateway.",
180
+ // Erase the precisely-typed builder to the `Capability` card surface (through
181
+ // `unknown`, as TypeBox tools are invariant in their parameter schema).
182
+ build: (ctx) => buildSaasCapability(ctx),
183
+ };
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Task capability — delegate a self-contained piece of work to a sub-agent.
3
+ *
4
+ * App-novel wiring. The `task` tool lets the primary agent hand off a bounded
5
+ * objective (e.g. "find every call site of `foo` and summarize them") to a fresh
6
+ * sub-agent that runs its own tool loop and reports back a single result, keeping
7
+ * the parent's context window clean.
8
+ *
9
+ * The actual sub-agent runner is NOT owned by this card — it is a framework /
10
+ * swarm concern injected through {@link DeckContext.framework} under the
11
+ * {@link DELEGATE_HANDLE_KEY} key. When that handle is present the capability
12
+ * delegates to it; when it is absent (tests, headless tooling, a host that has
13
+ * not wired the swarm) the capability degrades gracefully to a clearly-typed
14
+ * stub that reports delegation is unavailable rather than throwing. Either way
15
+ * the card builds a valid {@link Capability} and the deck typechecks and runs.
16
+ *
17
+ * The card produces a framework `AgentTool`; parameters are a TypeBox schema and
18
+ * the prose is original to this deck.
19
+ */
20
+ import { Type, type Static } from "@sinclair/typebox";
21
+ import { capabilityId, type Capability, type CapabilityCard, type DeckContext } from "../contract.js";
22
+
23
+ /** Key under which a host wires a live delegate runner into the deck context. */
24
+ export const DELEGATE_HANDLE_KEY = "delegate" as const;
25
+
26
+ /** A single delegated objective handed to the sub-agent runner. */
27
+ export interface DelegateRequest {
28
+ /** Optional named agent profile to run as (e.g. "explorer", "reviewer"). */
29
+ readonly agent?: string;
30
+ /** The self-contained objective the sub-agent should accomplish. */
31
+ readonly objective: string;
32
+ /** Optional extra context the parent wants the sub-agent to start with. */
33
+ readonly context?: string;
34
+ }
35
+
36
+ /** The single result a sub-agent reports back to the parent. */
37
+ export interface DelegateResult {
38
+ /** Whether the sub-agent considers the objective met. */
39
+ readonly ok: boolean;
40
+ /** The sub-agent's final report, surfaced to the parent agent verbatim. */
41
+ readonly report: string;
42
+ }
43
+
44
+ /** One line of a sub-agent's live activity stream (for the drill-in view). */
45
+ export interface ActivityLine {
46
+ /** What kind of step this is, used to colour it. */
47
+ readonly tone: "reason" | "tool" | "result";
48
+ /** The display text (already shortened to a single line). */
49
+ readonly text: string;
50
+ }
51
+
52
+ /** Live progress a running sub-agent streams back while it works. */
53
+ export interface DelegateProgress {
54
+ /** Cumulative tokens the sub-agent has spent so far (input + output). */
55
+ readonly tokens: number;
56
+ /** The sub-agent's recent activity (reasoning + tool steps), newest last. */
57
+ readonly activity: readonly ActivityLine[];
58
+ }
59
+
60
+ /**
61
+ * The contract a host's sub-agent runner must satisfy to be wired in.
62
+ *
63
+ * Intentionally minimal: the deck only needs a way to run one objective under
64
+ * cancellation and get one report back. How the runner spawns the sub-agent
65
+ * (in-process loop, worktree, separate process) is entirely the host's concern.
66
+ */
67
+ export interface DelegateRunner {
68
+ /** Optional roster of named agent profiles, surfaced in the tool description. */
69
+ listAgents?(): readonly string[];
70
+ /**
71
+ * Run one delegated objective and resolve with the sub-agent's report.
72
+ *
73
+ * When an `onProgress` callback is supplied the runner streams live metrics
74
+ * (token spend) as the sub-agent works, so the host can surface them (the
75
+ * background-agents panel). Optional — a runner may ignore it.
76
+ */
77
+ run(
78
+ request: DelegateRequest,
79
+ signal?: AbortSignal,
80
+ onProgress?: (progress: DelegateProgress) => void,
81
+ ): Promise<DelegateResult>;
82
+ }
83
+
84
+ const TaskParams = Type.Object(
85
+ {
86
+ objective: Type.String({
87
+ description:
88
+ "A complete, self-contained statement of what the sub-agent should accomplish. Include everything it needs; it does not share your conversation history.",
89
+ }),
90
+ agent: Type.Optional(
91
+ Type.String({
92
+ description:
93
+ "Optional named sub-agent profile to run as. Omit to use the default profile.",
94
+ }),
95
+ ),
96
+ context: Type.Optional(
97
+ Type.String({
98
+ description:
99
+ "Optional extra background the sub-agent should start with.",
100
+ }),
101
+ ),
102
+ },
103
+ { additionalProperties: false },
104
+ );
105
+
106
+ /** Statically-inferred parameter type the capability's `execute` receives. */
107
+ export type TaskParamsType = Static<typeof TaskParams>;
108
+
109
+ /** Structured detail returned alongside the model-facing content. */
110
+ export interface TaskDetails {
111
+ /** True when a delegate runner handled the objective. */
112
+ readonly delegated: boolean;
113
+ /** True when the sub-agent (or stub) considers the objective met. */
114
+ readonly ok: boolean;
115
+ /** The agent profile that ran, if one was named. */
116
+ readonly agent?: string;
117
+ /** Cumulative tokens the sub-agent spent — streamed live on progress updates. */
118
+ readonly tokens?: number;
119
+ /** The sub-agent's recent activity — streamed live for the drill-in view. */
120
+ readonly activity?: readonly ActivityLine[];
121
+ }
122
+
123
+ const STUB_NOTE =
124
+ "Sub-agent delegation is not wired in this environment, so the objective was not run. Wire a DelegateRunner into the deck context to enable it, or perform the work inline with the other tools.";
125
+
126
+ /** Read the optional DelegateRunner handle from the deck context. */
127
+ function readDelegateRunner(ctx: DeckContext): DelegateRunner | undefined {
128
+ const handle = ctx.framework?.[DELEGATE_HANDLE_KEY];
129
+ if (handle && typeof (handle as DelegateRunner).run === "function") {
130
+ return handle as DelegateRunner;
131
+ }
132
+ return undefined;
133
+ }
134
+
135
+ /** Compose the base tool description, appending the available sub-agent profiles. */
136
+ function baseDescription(agents: readonly string[]): string {
137
+ const roster =
138
+ agents.length > 0 ? ` Available sub-agent profiles: ${agents.join(", ")}.` : "";
139
+ return (
140
+ "Delegate a focused, self-contained piece of work to a sub-agent that runs its own tool loop and returns a single report. Use this to keep your own context clean when a task is well-scoped \u2014 searching a large codebase, drafting a file, or investigating a question end-to-end. Give a complete `objective`; the sub-agent does not see your conversation." +
141
+ roster
142
+ );
143
+ }
144
+
145
+ /**
146
+ * Build the task/delegate capability.
147
+ *
148
+ * If a {@link DelegateRunner} is present on the context it is bound and the tool
149
+ * truly delegates; otherwise the tool builds anyway and returns a typed,
150
+ * non-throwing stub result so the deck stays assemblable in every environment.
151
+ *
152
+ * @param ctx the deck context; an optional runner is read from
153
+ * `ctx.framework[DELEGATE_HANDLE_KEY]`.
154
+ */
155
+ export function buildTaskCapability(ctx: DeckContext): Capability<typeof TaskParams, TaskDetails> {
156
+ const runner = readDelegateRunner(ctx);
157
+ const agents = runner?.listAgents?.() ?? [];
158
+ return {
159
+ name: "task",
160
+ label: "Delegate task",
161
+ description: baseDescription(agents),
162
+ parameters: TaskParams,
163
+ async execute(_toolCallId, params, signal, onUpdate) {
164
+ if (!runner) {
165
+ return {
166
+ content: [{ type: "text", text: STUB_NOTE }],
167
+ details: { delegated: false, ok: false, agent: params.agent },
168
+ isError: true,
169
+ };
170
+ }
171
+ const result = await runner.run(
172
+ { objective: params.objective, agent: params.agent, context: params.context },
173
+ signal,
174
+ // Stream live token spend as a tool_update so the background-agents panel
175
+ // can show a running token count next to the elapsed clock. The `details`
176
+ // carries the structured `tokens`; the console reads it off the signal.
177
+ (progress) => {
178
+ onUpdate?.({
179
+ content: [{ type: "text", text: `${progress.tokens} tokens` }],
180
+ details: {
181
+ delegated: true,
182
+ ok: false,
183
+ agent: params.agent,
184
+ tokens: progress.tokens,
185
+ activity: progress.activity,
186
+ },
187
+ });
188
+ },
189
+ );
190
+ return {
191
+ content: [{ type: "text", text: result.report }],
192
+ details: { delegated: true, ok: result.ok, agent: params.agent },
193
+ isError: result.ok ? undefined : true,
194
+ };
195
+ },
196
+ };
197
+ }
198
+
199
+ /** Catalog row for the task/delegate capability. */
200
+ export const taskCard: CapabilityCard = {
201
+ id: capabilityId("task"),
202
+ title: "Delegate task",
203
+ summary: "Hand a self-contained objective to a sub-agent and receive one report back.",
204
+ // Erase the precisely-typed builder to the `Capability` card surface (through
205
+ // `unknown`, as TypeBox tools are invariant in their parameter schema).
206
+ build: (ctx) => buildTaskCapability(ctx),
207
+ };
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Todo capability — an in-memory checklist the agent set/reads during a run.
3
+ *
4
+ * App-novel and framework-agnostic: the store is a plain in-process array of
5
+ * items, owned by the card and closed over by the built capability. No file
6
+ * persistence, no session-branch reconstruction, no framework store — just a
7
+ * mutable list the agent rewrites wholesale (the model is expected to send the
8
+ * complete desired list on every `set`, the same convention coding agents use so
9
+ * the plan stays a single coherent snapshot rather than a diff stream).
10
+ *
11
+ * Two operations are folded into one tool keyed by an `action` discriminant:
12
+ * - `read` — return the current checklist as model-facing prose.
13
+ * - `set` — replace the checklist with the supplied items (the authoritative
14
+ * new plan), then echo it back.
15
+ *
16
+ * The card produces a {@link Capability} (the framework `AgentTool` shape) so the
17
+ * conductor consumes it verbatim as one of `options.tools`. Parameters are a
18
+ * TypeBox schema; the prose below is original to this deck.
19
+ */
20
+ import { Type, type Static } from "@sinclair/typebox";
21
+ import { capabilityId, type Capability, type CapabilityCard, type DeckContext } from "../contract.js";
22
+
23
+ /** Lifecycle state of one checklist item. */
24
+ export type TodoState = "pending" | "active" | "done" | "dropped";
25
+
26
+ /** Relative importance of one checklist item. */
27
+ export type TodoWeight = "low" | "normal" | "high";
28
+
29
+ /** One row of the in-memory checklist. */
30
+ export interface TodoItem {
31
+ /** The work to be done, phrased as a short imperative. */
32
+ readonly task: string;
33
+ /** Where the item stands right now. */
34
+ readonly state: TodoState;
35
+ /** How much it matters relative to the rest of the list. */
36
+ readonly weight: TodoWeight;
37
+ }
38
+
39
+ /**
40
+ * A minimal in-process checklist store.
41
+ *
42
+ * Deliberately tiny and synchronous: the whole list lives in one field, `set`
43
+ * swaps it atomically, and `read` hands back a frozen copy so a caller cannot
44
+ * mutate the store's backing array. One store is created per built capability,
45
+ * so two sessions never share a checklist.
46
+ */
47
+ export class TodoLedger {
48
+ private items: readonly TodoItem[] = [];
49
+
50
+ /** Replace the entire checklist with `next`; returns the stored snapshot. */
51
+ set(next: readonly TodoItem[]): readonly TodoItem[] {
52
+ this.items = Object.freeze([...next]);
53
+ return this.items;
54
+ }
55
+
56
+ /** Return the current checklist as a frozen, read-only snapshot. */
57
+ read(): readonly TodoItem[] {
58
+ return this.items;
59
+ }
60
+ }
61
+
62
+ const TodoItemSchema = Type.Object(
63
+ {
64
+ task: Type.String({ description: "Short imperative description of the work item." }),
65
+ state: Type.Union(
66
+ [
67
+ Type.Literal("pending"),
68
+ Type.Literal("active"),
69
+ Type.Literal("done"),
70
+ Type.Literal("dropped"),
71
+ ],
72
+ { description: "Lifecycle state of the item." },
73
+ ),
74
+ weight: Type.Union(
75
+ [Type.Literal("low"), Type.Literal("normal"), Type.Literal("high")],
76
+ { description: "Relative importance of the item." },
77
+ ),
78
+ },
79
+ { additionalProperties: false },
80
+ );
81
+
82
+ const TodoParams = Type.Object(
83
+ {
84
+ action: Type.Union([Type.Literal("read"), Type.Literal("set")], {
85
+ description:
86
+ "`read` returns the current checklist; `set` replaces it with the items you provide.",
87
+ }),
88
+ items: Type.Optional(
89
+ Type.Array(TodoItemSchema, {
90
+ description:
91
+ "The complete desired checklist. Required for `set` (send the whole list, not a delta); ignored for `read`.",
92
+ }),
93
+ ),
94
+ },
95
+ { additionalProperties: false },
96
+ );
97
+
98
+ /** Statically-inferred parameter type the capability's `execute` receives. */
99
+ export type TodoParamsType = Static<typeof TodoParams>;
100
+
101
+ /** Structured detail returned alongside the model-facing content. */
102
+ export interface TodoDetails {
103
+ /** The action that was performed. */
104
+ readonly action: "read" | "set";
105
+ /** The checklist as it stands after the call. */
106
+ readonly items: readonly TodoItem[];
107
+ }
108
+
109
+ const TODO_DESCRIPTION =
110
+ 'Maintain a working checklist for the current task. Call with `action:"set"` and a complete `items` list to record or revise your plan \u2014 always send the full intended list, since the previous one is discarded. Call with `action:"read"` to recall the current plan. Mark items `active` while in progress and `done` when finished so the list reflects real status; use `dropped` for work you decided to skip.';
111
+
112
+ const STATE_GLYPH: Record<TodoState, string> = {
113
+ pending: "[ ]",
114
+ active: "[~]",
115
+ done: "[x]",
116
+ dropped: "[-]",
117
+ };
118
+
119
+ /** Render the checklist as model-facing prose. */
120
+ function renderChecklist(items: readonly TodoItem[]): string {
121
+ if (items.length === 0) return "The checklist is empty.";
122
+ const lines = items.map((it) => {
123
+ const weight = it.weight === "normal" ? "" : ` (${it.weight})`;
124
+ return `${STATE_GLYPH[it.state]} ${it.task}${weight}`;
125
+ });
126
+ return lines.join("\n");
127
+ }
128
+
129
+ /**
130
+ * Build the todo capability, binding it to a freshly created in-memory ledger.
131
+ *
132
+ * The ledger is closed over by `execute`, so the checklist persists for the life
133
+ * of this capability instance (i.e. the session) without any external store.
134
+ *
135
+ * @param _ctx the deck context (unused — the todo card needs no cwd/backends)
136
+ */
137
+ export function buildTodoCapability(_ctx: DeckContext): Capability<typeof TodoParams, TodoDetails> {
138
+ const ledger = new TodoLedger();
139
+ return {
140
+ name: "todo",
141
+ label: "Checklist",
142
+ description: TODO_DESCRIPTION,
143
+ parameters: TodoParams,
144
+ async execute(_toolCallId, params) {
145
+ const items =
146
+ params.action === "set" ? ledger.set(params.items ?? []) : ledger.read();
147
+ const heading =
148
+ params.action === "set" ? "Checklist updated:" : "Current checklist:";
149
+ const text = `${heading}\n${renderChecklist(items)}`;
150
+ return {
151
+ content: [{ type: "text", text }],
152
+ details: { action: params.action, items },
153
+ };
154
+ },
155
+ };
156
+ }
157
+
158
+ /** Catalog row for the todo capability — registered in `CAPABILITY_CARDS`. */
159
+ export const todoCard: CapabilityCard = {
160
+ id: capabilityId("todo"),
161
+ title: "Checklist",
162
+ summary: "Keep an in-memory checklist of the current task's steps; set or read it.",
163
+ // The card surface is the erased `Capability`; the builder keeps its precise
164
+ // params/details type for direct callers and erases here. The erasure goes
165
+ // through `unknown` because TypeBox tools are invariant in their schema (the
166
+ // framework's own tool arrays are `AgentTool<any>[]` for the same reason).
167
+ build: (ctx) => buildTodoCapability(ctx),
168
+ };