@neurosquad/card-sdk 1.0.0 → 1.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,140 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@neurosquad/card-sdk` and to the card contract it speaks
4
+ (`CARD_SDK_CONTRACT_VERSION`) are listed here, newest first. The format follows
5
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the package uses
6
+ [Semantic Versioning](https://semver.org/).
7
+
8
+ Three versions matter to a card author:
9
+
10
+ - **SDK / contract version** (`SDK_VERSION`, `CARD_SDK_CONTRACT_VERSION`) — the
11
+ headings below. New methods and events arrive in minor versions.
12
+ - **Wire protocol** (`CARD_PROTOCOL_VERSION`, still **1**) — a card built for a
13
+ higher protocol is refused by an older app with `PROTOCOL_MISMATCH`.
14
+ - **NeuroSquad app version** — the host. Feature-detect with
15
+ `card.host.supports('<method>')`, or set `minAppVersion` in
16
+ `neurosquad-card.json` when the card cannot work without a method.
17
+
18
+ App versions below are the first public NeuroSquad release that has the change.
19
+
20
+ ## [Unreleased]
21
+
22
+ ### Changed (host, NeuroSquad 0.1.264)
23
+
24
+ - A card whose page process dies on its own (typically the machine running out
25
+ of memory) is reloaded automatically instead of staying a grey rectangle; the
26
+ new frame starts with `launch: 'reloaded'`. After three such reloads within
27
+ 10 minutes the card shows "The card stopped working" with **Reload**.
28
+ Nothing to do for authors — keep state you need across reloads in
29
+ `card.storage`.
30
+
31
+ ## [1.2.0] — 2026-10-04
32
+
33
+ Host: NeuroSquad **0.1.264** or newer (`card.host.supports('agents.timeline')`).
34
+
35
+ ### Added
36
+
37
+ - `card.agents.timeline(agentId, since, until?)` → `AgentTimeline`: one
38
+ arrow-connected AI agent's model requests (completion time; send time where
39
+ the harness's log records it; tokens; tool calls the model asked for), its
40
+ status as consecutive spans, and its turns — what the official Agent Pulse
41
+ card draws. `requestTimes` says what the log allows: `start-end`, `end` or
42
+ `none`. At most 2000 requests (the newest; `truncated`).
43
+ - Types `AgentTimeline`, `AgentRequestSpan`, `AgentTurnSpan`.
44
+ - Mock host: `agentTimeline` option and `setAgentTimeline(agentId, timeline)`.
45
+
46
+ ### Compatibility
47
+
48
+ - Same permission and scope as `agents.usage`: `usage.read` and an arrow to an
49
+ AI agent. No new permission.
50
+ - Rate limit 60 calls a minute per card.
51
+ - An older app answers `METHOD_NOT_FOUND`.
52
+
53
+ ## [1.1.0] — 2026-10-04
54
+
55
+ Host: NeuroSquad **0.1.264** or newer (`card.host.supports('agents.usage')`).
56
+
57
+ ### Added
58
+
59
+ - `card.agents.usage(agentId, since, until?)` → `AgentUsage`: one
60
+ arrow-connected AI agent's run inside a window — model requests, tokens by
61
+ kind (input, output, reasoning, cache read, cache write, total), prompts,
62
+ working and elapsed time, cost in micro-dollars (rounded up) with
63
+ `costPartial`, model and provider. What the official Run Stats card shows.
64
+ Timing comes from the app's own status history, so it is right even while
65
+ the card's frame was paused.
66
+ - Type `AgentUsage`.
67
+ - Mock host: `agentUsage` option and `setAgentUsage(agentId, usage)`.
68
+ - Contract types for the verified cards catalog (`VerifiedCatalog`,
69
+ `VerifiedEntry`, `validateVerifiedCatalog`, …) — first published here; they
70
+ were added to the source after the 1.0.0 package.
71
+
72
+ ### Changed
73
+
74
+ - A token count that is not reported is `null`, never `0`: a field the
75
+ harness's log does not carry, a harness without a readable usage log (Amp,
76
+ Cursor → `usageReadable: false`), a zero cache count from the user's own model
77
+ server, a zero cache write outside the Anthropic API. Show it as "not
78
+ reported".
79
+ - A cost of exactly 0 that a harness records for a model on the user's own
80
+ server means "no price": `costMicroUsd` is `null`, never $0.
81
+ - `prompts` does not count a finished turn without a single model request (a
82
+ status blip, such as a pasted prompt a CLI showed as busy before it really
83
+ submitted).
84
+
85
+ ### Compatibility
86
+
87
+ - Permission `usage.read` (already in 1.0; it now also unlocks this method for
88
+ agents connected by an arrow). Not connected → `NOT_CONNECTED`; a shell →
89
+ `INVALID_PARAMS`.
90
+ - Rate limit 60 calls a minute per card.
91
+ - An older app answers `METHOD_NOT_FOUND`. Wire protocol stays 1; cards built
92
+ with 1.0.0 run unchanged.
93
+
94
+ ### Host changes within contract 1.0 (no SDK version change)
95
+
96
+ Shipped in NeuroSquad releases between 1.0.0 and 1.1.0; listed because they can
97
+ change what a card sees.
98
+
99
+ - **0.1.253** — more manifest `name` values are reserved (the Graphify plugin's
100
+ tool family). **0.1.230** — reserved for the Memory, Context7 and Code Graph
101
+ plugins. **0.1.160** — for the RTK and caveman plugins. **0.1.141** —
102
+ `canvas_connect`, `canvas_disconnect`, `canvas_list`. **0.1.138** —
103
+ `agent_set_model`, `neurosquad_models`. A manifest with such a `name` fails
104
+ validation.
105
+ - **0.1.214** — `fs.*` refuses writes to more files that run code on the next
106
+ push or install: GitLab, Jenkins, CircleCI, Buildkite, Travis, Bitbucket,
107
+ Drone, AppVeyor and Azure Pipelines configs. `workspace.path` of a workspace
108
+ in WSL is this computer's view of it; for an SSH workspace it is `''` and
109
+ `fs.*` answers `UNAVAILABLE`. `usage.summary` rounds costs up to whole
110
+ micro-dollars, so a tiny priced cost never reads as 0. Linking a folder with
111
+ `neurosquad-card dev` needs developer mode on.
112
+ - **0.1.128** — `agents.lastReply` works for every harness with a readable
113
+ transcript, not only Claude Code, and an agent's built-in `reply` port
114
+ sends the final answer for all of them.
115
+ - **0.1.125** — the verified cards catalog (§18 of the spec): "Verified" badge
116
+ for cards listed at an exact commit and tree hash.
117
+
118
+ ## [1.0.0] — 2026-09-25
119
+
120
+ Host: NeuroSquad **0.1.123** or newer. First public release.
121
+
122
+ ### Added
123
+
124
+ - `connect()` → a typed `Card`: `card.call` / `card.on` over the whole
125
+ contract, plus namespaces for storage, settings, agents, terminals, ports,
126
+ tools for connected agents (MCP), network proxy, files, clipboard, usage
127
+ summary, theme, language, lifecycle and card UI (status, badge, overview,
128
+ attention, toasts, confirm).
129
+ - Manifest `neurosquad-card.json` (`manifestVersion: 1`) with its JSON Schema,
130
+ permissions with consent text, optional permissions requested at run time.
131
+ - React bindings (`@neurosquad/card-sdk/react`), the optional dark UI kit
132
+ (`ui.css`, `applyTheme()`), `createTranslator` for en/ru/zh.
133
+ - Mock host for tests (`@neurosquad/card-sdk/testing`).
134
+ - CLI `neurosquad-card`: `create` (vanilla and React templates), `validate`,
135
+ `pack`, `dev` (live reload into the running app).
136
+
137
+ [Unreleased]: https://docs.neurosquad.ai/en/card-sdk/changelog
138
+ [1.2.0]: https://docs.neurosquad.ai/en/card-sdk/changelog
139
+ [1.1.0]: https://docs.neurosquad.ai/en/card-sdk/changelog
140
+ [1.0.0]: https://www.npmjs.com/package/@neurosquad/card-sdk/v/1.0.0
package/README.md CHANGED
@@ -180,7 +180,7 @@ subscribed with the host automatically while a listener exists.
180
180
  | `card.ui` | `toast`, `confirm` → boolean, `setMenu([{ id, label, icon, onSelect }])`, `onMenu` |
181
181
  | `card.storage` | `get(key, fallback)`, `set`, `delete`, `keys(prefix)`, `clear`, `usage`; `card.storage.package.*` shared by every card of the package; `onChange` |
182
182
  | `card.settings` | `values`, `value(key)`, `hasSecret(key)`, `set(values)`, `open()`, `onChange` |
183
- | `card.agents` | `list`, `get`, `readScreen`, `lastReply`, `prompt(id, text, { whenBusy, submit })`, `onStatus`, `onTurn`, `onChanged`, `onOutput(ids, handler)` |
183
+ | `card.agents` | `list`, `get`, `readScreen`, `lastReply`, `prompt(id, text, { whenBusy, submit })`, `usage(id, since, until?)`, `timeline(id, since, until?)`, `onStatus`, `onTurn`, `onChanged`, `onOutput(ids, handler)` |
184
184
  | `card.terminals` | `run(id, command)` → `{ exitCode, output }`, `write(id, text, { submit })` |
185
185
  | `card.ports` | `inputs`, `outputs`, `peers`, `emit(output, data)`, `send`, `request`, `read`, `onMessage`, `onRequest(input, handler)`, `onPeersChanged` |
186
186
  | `card.tools` | `handle(name, handler)`, `setEnabled` |
@@ -238,6 +238,37 @@ await requestPermissions(card, ...needed.filter((id) => id !== null)) // never t
238
238
  await card.ports.emit('summary', text)
239
239
  ```
240
240
 
241
+ ### One agent's run: `card.agents.usage`
242
+
243
+ Since SDK 1.1 (NeuroSquad 0.1.264): the numbers of one AI agent connected to the
244
+ card by an arrow, inside a time window — model requests and tokens from the
245
+ harness's own log, prompts and working time from the app's status history (so
246
+ they are right for time your frame was off screen). Needs `usage.read`.
247
+
248
+ ```ts
249
+ if (await card.host.supports('agents.usage')) {
250
+ const run = await card.agents.usage(agentId, startedAt) // until = now
251
+ run.requests // model requests (API round-trips)
252
+ run.inputTokens, run.outputTokens, run.cacheReadTokens, run.cacheWriteTokens, run.totalTokens
253
+ run.prompts, run.workingMs, run.elapsedMs, run.costMicroUsd
254
+ }
255
+ ```
256
+
257
+ A `null` token field means **not reported** by that harness or provider (no
258
+ such field in its log, a local server that sends no cache counts, the OpenAI
259
+ APIs' missing cache writes) — show it as such, never as 0. The official
260
+ [Run Stats](https://github.com/glmn-ai/neurosquad-cards/tree/main/official-cards/run-stats)
261
+ card is built on it.
262
+
263
+ ### Requests on a timeline: `card.agents.timeline`
264
+
265
+ Since SDK 1.2 (NeuroSquad 0.1.264): the same arrow-connected agent's model
266
+ requests one by one — completion time, send time where the harness's log has
267
+ one (`requestTimes: 'start-end'`, otherwise `'end'`), tokens, tool calls — plus
268
+ its status as consecutive spans and its turns (prompt → finish). Needs
269
+ `usage.read`. The official [Agent Pulse](https://github.com/glmn-ai/neurosquad-cards/tree/main/official-cards/agent-pulse)
270
+ card draws it as swimlanes.
271
+
241
272
  ### Network
242
273
 
243
274
  ```ts
@@ -366,7 +397,7 @@ permissions, visibility, optionally rate limits) and records everything:
366
397
  `calls`, `storage`, `deliveries`, `prompts`, `logs`, `toasts`, `chrome`
367
398
  (title/status/badge/overview/menu). Drive the card with `setVisibility`,
368
399
  `setLanguage`, `setSettings`, `setSecret`, `sendPortMessage`, `requestPort`,
369
- `callTool`, `setAgentStatus`, `agentOutput`, `suspend`, `touchFile`. The same
400
+ `callTool`, `setAgentStatus`, `setAgentUsage`, `setAgentTimeline`, `agentOutput`, `suspend`, `touchFile`. The same
370
401
  mock powers the templates' browser preview when the page is opened outside the
371
402
  app.
372
403
 
@@ -378,7 +409,7 @@ that the value fits the input's schema; `permissions.request` fails with
378
409
  `NOT_VISIBLE` off screen; `fs.write` refuses the protected paths above. Options:
379
410
  `settings` (initial values), `secrets` (`{ key: value }` for `secret`
380
411
  settings — `{{secret:key}}` headers are filled, a missing one is
381
- `INVALID_PARAMS`), `enforceRateLimits` (per-method throttles; off by default so
412
+ `INVALID_PARAMS`), `agentUsage` / `agentTimeline` (what `agents.usage` / `agents.timeline` return per agent, or a function of the call), `enforceRateLimits` (per-method throttles; off by default so
382
413
  tests stay deterministic).
383
414
 
384
415
  **Test in the app too.** The mock runs in your page, not in the sandboxed
@@ -426,6 +457,10 @@ New methods and events arrive within a protocol version; check with
426
457
  `card.host.supports('method.name')`. A card built for a newer protocol is
427
458
  refused by an older app with `PROTOCOL_MISMATCH`.
428
459
 
460
+ What changed in each version — new methods, the NeuroSquad version they need,
461
+ permissions, migration notes — is in [CHANGELOG.md](./CHANGELOG.md) (also on
462
+ the docs site: https://docs.neurosquad.ai/en/card-sdk/changelog).
463
+
429
464
  ## License
430
465
 
431
466
  MIT
package/dist/card-sdk.js CHANGED
@@ -1,8 +1,8 @@
1
- /*! @neurosquad/card-sdk 1.0.0 | MIT */
1
+ /*! @neurosquad/card-sdk 1.2.0 | MIT */
2
2
 
3
3
  // src/contract/version.ts
4
4
  var CARD_PROTOCOL_VERSION = 1;
5
- var CARD_SDK_CONTRACT_VERSION = "1.0.0";
5
+ var CARD_SDK_CONTRACT_VERSION = "1.2.0";
6
6
  var MANIFEST_FILE = "neurosquad-card.json";
7
7
  var MANIFEST_VERSION = 1;
8
8
  var CARD_SCHEME = "nscard";
@@ -1756,7 +1756,10 @@ var PROTECTED_DIRS = /* @__PURE__ */ new Set([
1756
1756
  ".kilocode",
1757
1757
  ".windsurf",
1758
1758
  ".continue",
1759
- ".devcontainer"
1759
+ ".devcontainer",
1760
+ // CI that runs whatever the config says on the next push.
1761
+ ".circleci",
1762
+ ".buildkite"
1760
1763
  ]);
1761
1764
  var PROTECTED_FILES = /* @__PURE__ */ new Set([
1762
1765
  ".git",
@@ -1776,7 +1779,15 @@ var PROTECTED_FILES = /* @__PURE__ */ new Set([
1776
1779
  ".npmrc",
1777
1780
  ".yarnrc",
1778
1781
  ".yarnrc.yml",
1779
- ".pnpmfile.cjs"
1782
+ ".pnpmfile.cjs",
1783
+ ".gitlab-ci.yml",
1784
+ "azure-pipelines.yml",
1785
+ "jenkinsfile",
1786
+ ".travis.yml",
1787
+ "bitbucket-pipelines.yml",
1788
+ ".drone.yml",
1789
+ "appveyor.yml",
1790
+ ".appveyor.yml"
1780
1791
  ]);
1781
1792
  function protectedWriteReason(normalized) {
1782
1793
  if (normalized === "") return null;
@@ -2036,11 +2047,19 @@ var RESERVED_NAMES = /* @__PURE__ */ new Set([
2036
2047
  "squad",
2037
2048
  "mcp",
2038
2049
  "skill",
2050
+ "rtk",
2051
+ "caveman",
2052
+ "mem0",
2053
+ "codegraph",
2054
+ "graphify",
2055
+ "context7",
2056
+ "memory",
2039
2057
  "official"
2040
2058
  ]);
2041
2059
  var BUILTIN_MCP_TOOL_NAMES = [
2042
2060
  "agent_read",
2043
2061
  "agent_send",
2062
+ "agent_set_model",
2044
2063
  "agent_wait",
2045
2064
  "browser_click",
2046
2065
  "browser_history",
@@ -2049,14 +2068,47 @@ var BUILTIN_MCP_TOOL_NAMES = [
2049
2068
  "browser_screenshot",
2050
2069
  "browser_snapshot",
2051
2070
  "browser_type",
2071
+ "canvas_connect",
2072
+ "canvas_disconnect",
2073
+ "canvas_list",
2052
2074
  "canvas_remove_card",
2053
2075
  "canvas_spawn_card",
2076
+ "codegraph_check_index_coverage",
2077
+ "codegraph_detect_changes",
2078
+ "codegraph_get_architecture",
2079
+ "codegraph_get_code_snippet",
2080
+ "codegraph_get_file_outline",
2081
+ "codegraph_get_graph_schema",
2082
+ "codegraph_index_status",
2083
+ "codegraph_query_graph",
2084
+ "codegraph_reindex",
2085
+ "codegraph_search_code",
2086
+ "codegraph_search_graph",
2087
+ "codegraph_trace_path",
2088
+ "context7_query_docs",
2089
+ "context7_resolve_library_id",
2090
+ "graphify_affected",
2091
+ "graphify_community",
2092
+ "graphify_god_nodes",
2093
+ "graphify_neighbors",
2094
+ "graphify_node",
2095
+ "graphify_path",
2096
+ "graphify_query",
2097
+ "graphify_reindex",
2098
+ "graphify_stats",
2054
2099
  "kanban_add",
2055
2100
  "kanban_move",
2056
2101
  "kanban_read",
2057
2102
  "kanban_remove",
2058
2103
  "kanban_write",
2104
+ "memory_add",
2105
+ "memory_delete",
2106
+ "memory_get",
2107
+ "memory_list",
2108
+ "memory_search",
2109
+ "memory_update",
2059
2110
  "neurosquad_connections",
2111
+ "neurosquad_models",
2060
2112
  "note_append",
2061
2113
  "note_read",
2062
2114
  "note_write",
@@ -2593,6 +2645,13 @@ var CARD_VISIBILITIES = [
2593
2645
  "overview",
2594
2646
  "hidden"
2595
2647
  ];
2648
+ var CARD_AGENT_STATUSES = [
2649
+ "working",
2650
+ "needs-input",
2651
+ "finished",
2652
+ "idle",
2653
+ "exited"
2654
+ ];
2596
2655
  var EVENT_TOPICS = [
2597
2656
  "agents.status",
2598
2657
  "agents.turn",
@@ -3011,6 +3070,32 @@ var METHOD_SPECS = {
3011
3070
  permission: "usage.read",
3012
3071
  params: obj({ period: { enum: ["today", "7d", "30d"] } }, ["period"]),
3013
3072
  perMinute: 6
3073
+ },
3074
+ // Contract 1.1. Only agents connected to the card by an arrow (NOT_CONNECTED otherwise).
3075
+ "agents.usage": {
3076
+ permission: "usage.read",
3077
+ params: obj(
3078
+ {
3079
+ agentId: ID,
3080
+ since: { type: "integer", minimum: 0, maximum: 864e10 },
3081
+ until: { type: "integer", minimum: 0, maximum: 864e10 }
3082
+ },
3083
+ ["agentId", "since"]
3084
+ ),
3085
+ perMinute: 60
3086
+ },
3087
+ // Contract 1.2. Same scope as agents.usage: an arrow-connected AI agent.
3088
+ "agents.timeline": {
3089
+ permission: "usage.read",
3090
+ params: obj(
3091
+ {
3092
+ agentId: ID,
3093
+ since: { type: "integer", minimum: 0, maximum: 864e10 },
3094
+ until: { type: "integer", minimum: 0, maximum: 864e10 }
3095
+ },
3096
+ ["agentId", "since"]
3097
+ ),
3098
+ perMinute: 60
3014
3099
  }
3015
3100
  };
3016
3101
  var CARD_METHOD_NAMES = Object.keys(METHOD_SPECS);
@@ -3030,7 +3115,7 @@ function permissionStates(declared, granted, resolveReason) {
3030
3115
  }
3031
3116
 
3032
3117
  // src/version.ts
3033
- var SDK_VERSION = "1.0.0";
3118
+ var SDK_VERSION = "1.2.0";
3034
3119
 
3035
3120
  // src/client/base64.ts
3036
3121
  function bytesToBase64(bytes) {
@@ -4434,6 +4519,36 @@ var CardAgents = class {
4434
4519
  lastReply(agentId) {
4435
4520
  return this.card.call("agents.lastReply", { agentId });
4436
4521
  }
4522
+ /**
4523
+ * One connected AI agent's run since `since` (epoch ms) — `usage.read`,
4524
+ * contract 1.1: check `card.host.supports('agents.usage')` first. Model
4525
+ * requests and tokens come from the harness's own log (the app's Usage
4526
+ * data), prompts and working time from the app's status history, so the
4527
+ * numbers are right even for time the card was off screen. A `null`
4528
+ * token field is not reported by that harness or provider — show it as
4529
+ * such, never as 0.
4530
+ */
4531
+ usage(agentId, since, until) {
4532
+ return this.card.call("agents.usage", {
4533
+ agentId,
4534
+ since,
4535
+ ...until !== void 0 ? { until } : {}
4536
+ });
4537
+ }
4538
+ /**
4539
+ * One connected AI agent's model requests and statuses since `since` —
4540
+ * `usage.read`, contract 1.2: check `card.host.supports('agents.timeline')`.
4541
+ * Each request has its completion time, its send time where the harness's
4542
+ * log records one (`requestTimes: 'start-end'`), tokens and tool calls;
4543
+ * statuses are consecutive spans and turns carry prompt / finish times.
4544
+ */
4545
+ timeline(agentId, since, until) {
4546
+ return this.card.call("agents.timeline", {
4547
+ agentId,
4548
+ since,
4549
+ ...until !== void 0 ? { until } : {}
4550
+ });
4551
+ }
4437
4552
  /**
4438
4553
  * Sends a prompt to a connected AI agent (`agents.prompt`, 6 per minute).
4439
4554
  * While the agent works it is queued (`whenBusy: 'queue'`, default),
@@ -5131,6 +5246,7 @@ export {
5131
5246
  BUILTIN_CARD_PORTS,
5132
5247
  BUILTIN_MCP_TOOL_NAMES,
5133
5248
  BUILTIN_PEER_KINDS,
5249
+ CARD_AGENT_STATUSES,
5134
5250
  CARD_ERROR_CODES,
5135
5251
  CARD_EVENT_NAMES,
5136
5252
  CARD_HOST_ID_RE,
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- /*! @neurosquad/card-sdk 1.0.0 | MIT */
2
+ /*! @neurosquad/card-sdk 1.2.0 | MIT */
3
3
 
4
4
  // src/version.ts
5
- var SDK_VERSION = "1.0.0";
5
+ var SDK_VERSION = "1.2.0";
6
6
 
7
7
  // src/cli/create.ts
8
8
  import {
@@ -1421,11 +1421,19 @@ var RESERVED_NAMES = /* @__PURE__ */ new Set([
1421
1421
  "squad",
1422
1422
  "mcp",
1423
1423
  "skill",
1424
+ "rtk",
1425
+ "caveman",
1426
+ "mem0",
1427
+ "codegraph",
1428
+ "graphify",
1429
+ "context7",
1430
+ "memory",
1424
1431
  "official"
1425
1432
  ]);
1426
1433
  var BUILTIN_MCP_TOOL_NAMES = [
1427
1434
  "agent_read",
1428
1435
  "agent_send",
1436
+ "agent_set_model",
1429
1437
  "agent_wait",
1430
1438
  "browser_click",
1431
1439
  "browser_history",
@@ -1434,14 +1442,47 @@ var BUILTIN_MCP_TOOL_NAMES = [
1434
1442
  "browser_screenshot",
1435
1443
  "browser_snapshot",
1436
1444
  "browser_type",
1445
+ "canvas_connect",
1446
+ "canvas_disconnect",
1447
+ "canvas_list",
1437
1448
  "canvas_remove_card",
1438
1449
  "canvas_spawn_card",
1450
+ "codegraph_check_index_coverage",
1451
+ "codegraph_detect_changes",
1452
+ "codegraph_get_architecture",
1453
+ "codegraph_get_code_snippet",
1454
+ "codegraph_get_file_outline",
1455
+ "codegraph_get_graph_schema",
1456
+ "codegraph_index_status",
1457
+ "codegraph_query_graph",
1458
+ "codegraph_reindex",
1459
+ "codegraph_search_code",
1460
+ "codegraph_search_graph",
1461
+ "codegraph_trace_path",
1462
+ "context7_query_docs",
1463
+ "context7_resolve_library_id",
1464
+ "graphify_affected",
1465
+ "graphify_community",
1466
+ "graphify_god_nodes",
1467
+ "graphify_neighbors",
1468
+ "graphify_node",
1469
+ "graphify_path",
1470
+ "graphify_query",
1471
+ "graphify_reindex",
1472
+ "graphify_stats",
1439
1473
  "kanban_add",
1440
1474
  "kanban_move",
1441
1475
  "kanban_read",
1442
1476
  "kanban_remove",
1443
1477
  "kanban_write",
1478
+ "memory_add",
1479
+ "memory_delete",
1480
+ "memory_get",
1481
+ "memory_list",
1482
+ "memory_search",
1483
+ "memory_update",
1444
1484
  "neurosquad_connections",
1485
+ "neurosquad_models",
1445
1486
  "note_append",
1446
1487
  "note_read",
1447
1488
  "note_write",
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @neurosquad/card-sdk 1.0.0 | MIT */
1
+ /*! @neurosquad/card-sdk 1.2.0 | MIT */
2
2
 
3
3
  // src/react/index.tsx
4
4
  import {
package/dist/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @neurosquad/card-sdk 1.0.0 | MIT */
1
+ /*! @neurosquad/card-sdk 1.2.0 | MIT */
2
2
 
3
3
  // src/testing/mockHost.ts
4
4
  import {
@@ -422,6 +422,18 @@ var MockHost = class {
422
422
  this.emit("tools.cancel", { callId });
423
423
  pending?.reject(new HostError("USER_CANCELLED", "cancelled"));
424
424
  }
425
+ /** What `agents.timeline` returns for this agent from now on (merged over an empty timeline). */
426
+ setAgentTimeline(agentId, timeline) {
427
+ const current = this.options.agentTimeline;
428
+ const table = typeof current === "object" && current !== null ? current : {};
429
+ this.options.agentTimeline = { ...table, [agentId]: timeline };
430
+ }
431
+ /** What `agents.usage` returns for this agent from now on (merged over an all-zero run). */
432
+ setAgentUsage(agentId, usage) {
433
+ const current = this.options.agentUsage;
434
+ const table = typeof current === "object" && current !== null ? current : {};
435
+ this.options.agentUsage = { ...table, [agentId]: usage };
436
+ }
425
437
  /**
426
438
  * Changes an agent's status; the card gets `agents.status` if subscribed,
427
439
  * and `agents.turn` by the app's rule: `start` on entering `working`,
@@ -854,6 +866,64 @@ var MockHost = class {
854
866
  case "clipboard.writeText":
855
867
  this.clipboard.push(str("text"));
856
868
  return void 0;
869
+ case "agents.usage": {
870
+ const agent = this.connectedAgent(str("agentId"));
871
+ if (agent.kind !== "ai") fail("INVALID_PARAMS", `${agent.name} is not an AI agent`);
872
+ const since = p["since"];
873
+ const until = p["until"] ?? Date.now();
874
+ if (since > until) fail("INVALID_PARAMS", '"since" is after "until"');
875
+ const source = this.options.agentUsage;
876
+ const given = typeof source === "function" ? source(agent.id, since, until) : source?.[agent.id] ?? {};
877
+ const base = {
878
+ agentId: agent.id,
879
+ harness: agent.harness,
880
+ since,
881
+ until,
882
+ status: agent.status,
883
+ usageReadable: true,
884
+ requests: 0,
885
+ inputTokens: 0,
886
+ outputTokens: 0,
887
+ cacheReadTokens: 0,
888
+ cacheWriteTokens: 0,
889
+ reasoningTokens: 0,
890
+ totalTokens: 0,
891
+ costMicroUsd: null,
892
+ costPartial: false,
893
+ ...agent.model ? { model: agent.model } : {},
894
+ prompts: 0,
895
+ workingMs: 0,
896
+ firstPromptAt: null,
897
+ lastFinishedAt: null,
898
+ elapsedMs: null,
899
+ timingComplete: true,
900
+ scannedAt: Date.now()
901
+ };
902
+ return { ...base, ...given };
903
+ }
904
+ case "agents.timeline": {
905
+ const agent = this.connectedAgent(str("agentId"));
906
+ if (agent.kind !== "ai") fail("INVALID_PARAMS", `${agent.name} is not an AI agent`);
907
+ const since = p["since"];
908
+ const until = p["until"] ?? Date.now();
909
+ if (since > until) fail("INVALID_PARAMS", '"since" is after "until"');
910
+ const source = this.options.agentTimeline;
911
+ const given = typeof source === "function" ? source(agent.id, since, until) : source?.[agent.id] ?? {};
912
+ const base = {
913
+ agentId: agent.id,
914
+ harness: agent.harness,
915
+ since,
916
+ until,
917
+ usageReadable: true,
918
+ requestTimes: "start-end",
919
+ requests: [],
920
+ truncated: false,
921
+ statuses: [{ status: agent.status, from: since, to: until }],
922
+ turns: [],
923
+ scannedAt: Date.now()
924
+ };
925
+ return { ...base, ...given };
926
+ }
857
927
  case "usage.summary":
858
928
  return this.options.usage ?? {
859
929
  period: str("period"),
@@ -1,4 +1,4 @@
1
- import { type AgentInfo, type CardEventName, type CardEvents, type CardInstanceInfo, type CardMethodName, type CardSize, type CardTone, type CardVisibility, type EventTopic, type FsEntry, type FsStat, type HostContext, type HostIconName, type I18nSnapshot, type JsonValue, type MenuItem, type MethodParams, type MethodResult, type OverviewContent, type PeerInfo, type PermissionId, type PermissionState, type PortInfo, type SettingsSnapshot, type SettingValue, type ThemeSnapshot, type ToolResultPayload, type UsageSummary, type WorkspaceInfo } from '../contract/index.js';
1
+ import { type AgentInfo, type AgentTimeline, type AgentUsage, type CardEventName, type CardEvents, type CardInstanceInfo, type CardMethodName, type CardSize, type CardTone, type CardVisibility, type EventTopic, type FsEntry, type FsStat, type HostContext, type HostIconName, type I18nSnapshot, type JsonValue, type MenuItem, type MethodParams, type MethodResult, type OverviewContent, type PeerInfo, type PermissionId, type PermissionState, type PortInfo, type SettingsSnapshot, type SettingValue, type ThemeSnapshot, type ToolResultPayload, type UsageSummary, type WorkspaceInfo } from '../contract/index.js';
2
2
  import type { CallOptions, Channel } from './channel.js';
3
3
  import { CardResponse, ChunkQueue, type CardFetchInit } from './net.js';
4
4
  import { type ThemeTarget } from './theme.js';
@@ -415,6 +415,24 @@ export declare class CardAgents {
415
415
  text: string | null;
416
416
  at: number | null;
417
417
  }>;
418
+ /**
419
+ * One connected AI agent's run since `since` (epoch ms) — `usage.read`,
420
+ * contract 1.1: check `card.host.supports('agents.usage')` first. Model
421
+ * requests and tokens come from the harness's own log (the app's Usage
422
+ * data), prompts and working time from the app's status history, so the
423
+ * numbers are right even for time the card was off screen. A `null`
424
+ * token field is not reported by that harness or provider — show it as
425
+ * such, never as 0.
426
+ */
427
+ usage(agentId: string, since: number, until?: number): Promise<AgentUsage>;
428
+ /**
429
+ * One connected AI agent's model requests and statuses since `since` —
430
+ * `usage.read`, contract 1.2: check `card.host.supports('agents.timeline')`.
431
+ * Each request has its completion time, its send time where the harness's
432
+ * log records one (`requestTimes: 'start-end'`), tokens and tool calls;
433
+ * statuses are consecutive spans and turns carry prompt / finish times.
434
+ */
435
+ timeline(agentId: string, since: number, until?: number): Promise<AgentTimeline>;
418
436
  /**
419
437
  * Sends a prompt to a connected AI agent (`agents.prompt`, 6 per minute).
420
438
  * While the agent works it is queued (`whenBusy: 'queue'`, default),
@@ -17,6 +17,8 @@ export type CardVisibility =
17
17
  | 'hidden';
18
18
  export declare const CARD_VISIBILITIES: readonly CardVisibility[];
19
19
  export type AgentStatus = 'working' | 'needs-input' | 'finished' | 'idle' | 'exited';
20
+ /** Every AgentStatus. Append only: main persists statuses by their index here. */
21
+ export declare const CARD_AGENT_STATUSES: readonly AgentStatus[];
20
22
  export interface CardInstanceInfo {
21
23
  /** The card's id on the canvas (the Agent record id). */
22
24
  instanceId: string;
@@ -216,6 +218,119 @@ export interface UsageSummary {
216
218
  /** Some rows had no price and are not in the total. */
217
219
  partial: boolean;
218
220
  }
221
+ /**
222
+ * One agent's run inside a time window — `agents.usage` (spec §8.6.1). The
223
+ * token numbers are the model requests the harness itself logged (the Usage
224
+ * section's data), the timing comes from the app's status history.
225
+ *
226
+ * A token field is `null` when it is **not reported**: the harness's log has
227
+ * no such field, or the provider sends none (a local or custom server that
228
+ * reports no cache, the OpenAI APIs that have no cache writes) — never a 0
229
+ * that would read as a measurement. Every number is an integer.
230
+ */
231
+ export interface AgentUsage {
232
+ agentId: string;
233
+ harness: string;
234
+ /** The window: epoch ms, `since` inclusive. `until` is now unless the call gave one. */
235
+ since: number;
236
+ until: number;
237
+ /** The agent's status at `until`, as far as the app's history knows. */
238
+ status: AgentStatus;
239
+ /** False when the app reads no usage log for this harness (Amp, Cursor): every token field is null. */
240
+ usageReadable: boolean;
241
+ /**
242
+ * Model requests (API round-trips) the harness logged in the window. Null
243
+ * when its log keeps running totals rather than requests (Hermes, Factory
244
+ * Droid) or when usage is not readable.
245
+ */
246
+ requests: number | null;
247
+ /** Uncached input (prompt) tokens. */
248
+ inputTokens: number | null;
249
+ /** Output tokens, reasoning included. */
250
+ outputTokens: number | null;
251
+ cacheReadTokens: number | null;
252
+ cacheWriteTokens: number | null;
253
+ /** The reasoning part of `outputTokens`. */
254
+ reasoningTokens: number | null;
255
+ /** input + output + cache read + cache write (the reported ones). */
256
+ totalTokens: number | null;
257
+ /** Micro-dollars, rounded up; null when no request in the window has a known price. */
258
+ costMicroUsd: number | null;
259
+ /** Some requests had no known price and are not in `costMicroUsd`. */
260
+ costPartial: boolean;
261
+ /** The model of the newest request in the window (else the agent's configured one). */
262
+ model?: string;
263
+ /** Who served it: `anthropic`, `openai`, `openrouter`, a custom provider's name… */
264
+ provider?: string;
265
+ /** Turns the agent was given (a permission answer continues the same turn). */
266
+ prompts: number;
267
+ /** Time spent working inside the window (waiting on the user excluded). */
268
+ workingMs: number;
269
+ firstPromptAt: number | null;
270
+ lastFinishedAt: number | null;
271
+ /** First prompt → latest finish (→ `until` while a turn is under way); null before the first prompt. */
272
+ elapsedMs: number | null;
273
+ /**
274
+ * False when the app's status history starts after `since` (the agent or
275
+ * the app was started later, or old history was trimmed): the timing counts
276
+ * only what the app saw.
277
+ */
278
+ timingComplete: boolean;
279
+ /** When the usage logs were last read (epoch ms). */
280
+ scannedAt: number;
281
+ }
282
+ /** One model request on an agent's timeline (`agents.timeline`). */
283
+ export interface AgentRequestSpan {
284
+ /** When the response completed (epoch ms) — always known. */
285
+ endedAt: number;
286
+ /** When the request was sent; null when the harness's log does not say (`AgentTimeline.requestTimes`). */
287
+ startedAt: number | null;
288
+ model?: string;
289
+ /** Same "not reported" rules as AgentUsage, per request. */
290
+ inputTokens: number | null;
291
+ outputTokens: number | null;
292
+ cacheReadTokens: number | null;
293
+ cacheWriteTokens: number | null;
294
+ /** Tool calls the model asked for in this response, in order (where the log names them). */
295
+ tools?: string[];
296
+ }
297
+ /** A turn (a prompt and what the agent did with it) on the timeline. */
298
+ export interface AgentTurnSpan {
299
+ start: number;
300
+ /** Null while under way. */
301
+ end: number | null;
302
+ endedAs?: AgentStatus;
303
+ /** False for a turn that ended without one model request (a status blip; not a prompt). */
304
+ counted: boolean;
305
+ }
306
+ /**
307
+ * One connected agent's requests and statuses inside a window —
308
+ * `agents.timeline` (spec §8.6.2): every model request with its start, end,
309
+ * tokens and tool calls (from the harness's own log), the agent's status as
310
+ * consecutive spans and its turns (from the app's status history).
311
+ */
312
+ export interface AgentTimeline {
313
+ agentId: string;
314
+ harness: string;
315
+ since: number;
316
+ until: number;
317
+ usageReadable: boolean;
318
+ /**
319
+ * `start-end`: requests carry their send time (bars); `end`: completion
320
+ * times only (ticks); `none`: no readable request log.
321
+ */
322
+ requestTimes: 'start-end' | 'end' | 'none';
323
+ /** Oldest first; at most 2000 (the newest are kept and `truncated` is set). */
324
+ requests: AgentRequestSpan[];
325
+ truncated: boolean;
326
+ statuses: {
327
+ status: AgentStatus;
328
+ from: number;
329
+ to: number;
330
+ }[];
331
+ turns: AgentTurnSpan[];
332
+ scannedAt: number;
333
+ }
219
334
  export type EventTopic = 'agents.status' | 'agents.turn' | 'agents.changed' | 'agents.output' | 'storage.changed';
220
335
  export declare const EVENT_TOPICS: readonly EventTopic[];
221
336
  /** Permission each subscribable topic needs. */
@@ -686,6 +801,24 @@ export interface CardMethods {
686
801
  };
687
802
  result: UsageSummary;
688
803
  };
804
+ /** Added within protocol 1 (contract 1.1): check `card.host.supports('agents.usage')`. */
805
+ 'agents.usage': {
806
+ params: {
807
+ agentId: string;
808
+ since: number;
809
+ until?: number;
810
+ };
811
+ result: AgentUsage;
812
+ };
813
+ /** Added within protocol 1 (contract 1.2): check `card.host.supports('agents.timeline')`. */
814
+ 'agents.timeline': {
815
+ params: {
816
+ agentId: string;
817
+ since: number;
818
+ until?: number;
819
+ };
820
+ result: AgentTimeline;
821
+ };
689
822
  }
690
823
  export type CardMethodName = keyof CardMethods;
691
824
  export type MethodParams<M extends CardMethodName> = CardMethods[M]['params'];
@@ -96,7 +96,8 @@ export declare function isGitInternalPath(normalized: string): boolean;
96
96
  * agents or developer tools run commands without a build step (MC-10): agent
97
97
  * hooks and MCP configs (`.claude/`, `.mcp.json`, …), agent instructions
98
98
  * (`CLAUDE.md`, `AGENTS.md`, …: persistent prompt injection), editor tasks
99
- * (`.vscode/`), git hooks managers (`.husky/`), CI (`.github/workflows/`),
99
+ * (`.vscode/`), git hooks managers (`.husky/`), CI (`.github/workflows/`,
100
+ * `.gitlab-ci.yml`, `Jenkinsfile`, `.circleci/`, …),
100
101
  * direnv and package-manager rc files. Case-insensitive.
101
102
  */
102
103
  export declare function protectedWriteReason(normalized: string): string | null;
@@ -0,0 +1,136 @@
1
+ import type { CardLanguage } from './localized.js';
2
+ import { type PermissionId } from './permissions.js';
3
+ export declare const VERIFIED_SCHEMA_VERSION = 1;
4
+ /** Where the catalog lives (a public repository of the official organization). */
5
+ export declare const VERIFIED_REPO: {
6
+ owner: string;
7
+ repo: string;
8
+ file: string;
9
+ };
10
+ export declare const VERIFIED_CATEGORIES: readonly ["agents", "productivity", "dev-tools", "data", "integrations", "fun", "other"];
11
+ export type VerifiedCategory = (typeof VERIFIED_CATEGORIES)[number];
12
+ export declare const VERIFIED_LIMITS: {
13
+ readonly fileBytes: number;
14
+ readonly entries: 5000;
15
+ readonly id: 64;
16
+ readonly name: 80;
17
+ readonly description: 600;
18
+ readonly notes: 1000;
19
+ readonly author: 100;
20
+ readonly reviewer: 100;
21
+ readonly license: 64;
22
+ readonly url: 500;
23
+ readonly version: 64;
24
+ readonly tag: 32;
25
+ readonly tags: 16;
26
+ readonly hosts: 32;
27
+ readonly screenshots: 8;
28
+ /** How many skipped entries are reported (the rest are only counted). */
29
+ readonly reportedSkips: 50;
30
+ };
31
+ /** Text in the app's three languages; `en` is required and the fallback. */
32
+ export type VerifiedText = {
33
+ en: string;
34
+ } & Partial<Record<Exclude<CardLanguage, 'en'>, string>>;
35
+ export interface VerifiedEntry {
36
+ /** Catalog slug, unique: `[a-z0-9-]{2,64}`. */
37
+ id: string;
38
+ /** The package's manifest `name` (checked against the downloaded manifest at install). */
39
+ packageId: string;
40
+ name: VerifiedText;
41
+ description: VerifiedText;
42
+ author: {
43
+ name: string;
44
+ github?: string;
45
+ };
46
+ /** `owner/repo`, lowercased (GitHub names are case-insensitive). */
47
+ repo: string;
48
+ /** Package folder inside the repository (POSIX, case kept); absent = the root. */
49
+ path?: string;
50
+ /** Manifest version that was reviewed. */
51
+ version: string;
52
+ /** The reviewed commit (40 hex). */
53
+ commit: string;
54
+ /** sha256 hex of the package folder at that commit (the installer's tree hash). */
55
+ treeHash: string;
56
+ permissions: PermissionId[];
57
+ optionalPermissions: PermissionId[];
58
+ networkHosts: string[];
59
+ tags: string[];
60
+ category: VerifiedCategory;
61
+ license: string;
62
+ homepage?: string;
63
+ /** Paths inside the package. */
64
+ icon?: string;
65
+ screenshots: string[];
66
+ minAppVersion?: string;
67
+ /** Informational: the entry says it is the team's own card (the Official badge is still earned from GitHub). */
68
+ official: boolean;
69
+ /** `YYYY-MM-DD` or an ISO date-time. */
70
+ verifiedAt: string;
71
+ reviewer: string;
72
+ notes?: VerifiedText;
73
+ }
74
+ export interface VerifiedCatalog {
75
+ schemaVersion: 1;
76
+ updatedAt?: string;
77
+ cards: VerifiedEntry[];
78
+ }
79
+ export interface VerifiedSkip {
80
+ index: number;
81
+ id?: string;
82
+ reason: string;
83
+ }
84
+ export type VerifiedParseResult = {
85
+ ok: true;
86
+ catalog: VerifiedCatalog;
87
+ skipped: VerifiedSkip[];
88
+ skippedCount: number;
89
+ } | {
90
+ ok: false;
91
+ code: 'too-large' | 'not-json' | 'bad-shape' | 'unsupported-version';
92
+ error: string;
93
+ };
94
+ /** Removes control, bidi and invisible characters and trims. */
95
+ export declare function cleanVerifiedText(text: string, multiline?: boolean): string;
96
+ /**
97
+ * Validates a parsed `verified.json`. Invalid entries, a second entry with
98
+ * the same id and a second entry for the same source (repo + path) are
99
+ * skipped; the rest is kept.
100
+ */
101
+ export declare function validateVerifiedCatalog(raw: unknown): VerifiedParseResult;
102
+ /** Parses the file's text (size-checked) and validates it. */
103
+ export declare function parseVerifiedText(textValue: string): VerifiedParseResult;
104
+ /** `owner/repo[/path]` — owner and repo lowercased, the path as written. */
105
+ export declare function verifiedSourceKey(repo: string, path?: string): string;
106
+ /** The catalog entry for a GitHub package source, if any. */
107
+ export declare function findVerifiedEntry(entries: readonly VerifiedEntry[], source: {
108
+ owner: string;
109
+ repo: string;
110
+ subdir?: string;
111
+ }): VerifiedEntry | undefined;
112
+ /**
113
+ * How an installed package relates to its catalog entry:
114
+ * - `verified`: same commit and the installed files hash to the reviewed tree hash
115
+ * - `mismatch`: same commit, different files (never shown as verified)
116
+ * - `update`: the catalog points at a newer version
117
+ * - `other-version`: another commit that is not newer (e.g. a branch head)
118
+ */
119
+ export type VerifiedStatus = 'verified' | 'mismatch' | 'update' | 'other-version';
120
+ export declare function verifiedStatusFor(entry: VerifiedEntry, installed: {
121
+ commit: string | null;
122
+ treeHash: string;
123
+ version: string;
124
+ }): VerifiedStatus;
125
+ /** What "Install" resolves: the entry's repo and folder pinned to the reviewed commit. */
126
+ export declare function verifiedInstallInput(entry: VerifiedEntry): string;
127
+ /** SemVer precedence of the release part (pre-release sorts before the release). */
128
+ export declare function compareSemver(a: string, b: string): number;
129
+ /**
130
+ * Local search: every word of the query must appear somewhere (names in all
131
+ * three languages, descriptions, tags, author); entries whose name matches
132
+ * come first, otherwise the catalog order is kept.
133
+ */
134
+ export declare function searchVerified(entries: readonly VerifiedEntry[], query: string, category?: VerifiedCategory | null): VerifiedEntry[];
135
+ /** The entry's text in `language`, falling back to English. */
136
+ export declare function verifiedText(value: VerifiedText | undefined, language: CardLanguage): string;
@@ -1,7 +1,7 @@
1
1
  /** Major version of the wire protocol. A card built for a higher one is refused with PROTOCOL_MISMATCH. */
2
2
  export declare const CARD_PROTOCOL_VERSION = 1;
3
3
  /** Version of this contract (and of `@neurosquad/card-sdk`'s protocol layer). */
4
- export declare const CARD_SDK_CONTRACT_VERSION = "1.0.0";
4
+ export declare const CARD_SDK_CONTRACT_VERSION = "1.2.0";
5
5
  /** The manifest at the root of every card package. */
6
6
  export declare const MANIFEST_FILE = "neurosquad-card.json";
7
7
  /** `manifestVersion` this app reads. */
@@ -1,4 +1,4 @@
1
- import { type AgentInfo, type CardEvents, type CardEventName, type CardLanguage, type CardManifest, type CardMethodName, type CardSize, type CardVisibility, type EventTopic, type HostContext, type JsonValue, type MethodParams, type MethodResult, type NormalizedManifest, type PeerInfo, type PermissionId, type PortInfo, type SettingValue, type ThemeSnapshot, type ToolResultPayload, type UsageSummary, type Card, type ConnectOptions } from '../index.js';
1
+ import { type AgentInfo, type AgentTimeline, type AgentUsage, type CardEvents, type CardEventName, type CardLanguage, type CardManifest, type CardMethodName, type CardSize, type CardVisibility, type EventTopic, type HostContext, type JsonValue, type MethodParams, type MethodResult, type NormalizedManifest, type PeerInfo, type PermissionId, type PortInfo, type SettingValue, type ThemeSnapshot, type ToolResultPayload, type UsageSummary, type Card, type ConnectOptions } from '../index.js';
2
2
  /** A peer card in the mock: its description plus optional behaviour. */
3
3
  export interface MockPeer extends PeerInfo {
4
4
  /** Retained output values (`ports.read`). */
@@ -66,6 +66,13 @@ export interface MockHostOptions {
66
66
  enforceRateLimits?: boolean;
67
67
  /** Usage summary for `usage.summary`. */
68
68
  usage?: UsageSummary;
69
+ /**
70
+ * What `agents.usage` returns per agent id (merged over an all-zero run of
71
+ * that agent), or a function of the call. Change it later with `setAgentUsage`.
72
+ */
73
+ /** What `agents.timeline` returns per agent id (merged over an empty timeline), or a function of the call. */
74
+ agentTimeline?: Record<string, Partial<AgentTimeline>> | ((agentId: string, since: number, until: number) => Partial<AgentTimeline>);
75
+ agentUsage?: Record<string, Partial<AgentUsage>> | ((agentId: string, since: number, until: number) => Partial<AgentUsage>);
69
76
  /**
70
77
  * Initial setting values (as if the user had saved them in the host form).
71
78
  * Same as calling `setSettings(values)` before `connect()`.
@@ -268,6 +275,10 @@ export declare class MockHost {
268
275
  };
269
276
  /** Cancels a running tool call (`tools.cancel`); the promise from callTool rejects. */
270
277
  cancelTool(callId: string): void;
278
+ /** What `agents.timeline` returns for this agent from now on (merged over an empty timeline). */
279
+ setAgentTimeline(agentId: string, timeline: Partial<AgentTimeline>): void;
280
+ /** What `agents.usage` returns for this agent from now on (merged over an all-zero run). */
281
+ setAgentUsage(agentId: string, usage: Partial<AgentUsage>): void;
271
282
  /**
272
283
  * Changes an agent's status; the card gets `agents.status` if subscribed,
273
284
  * and `agents.turn` by the app's rule: `start` on entering `working`,
@@ -1,2 +1,2 @@
1
1
  /** Version of `@neurosquad/card-sdk` (sent to the host in `host.ready`). Kept equal to package.json by a test. */
2
- export declare const SDK_VERSION = "1.0.0";
2
+ export declare const SDK_VERSION = "1.2.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neurosquad/card-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Build custom cards for NeuroSquad: a typed client for the card API, React hooks, an optional dark UI kit and the neurosquad-card CLI (create, dev, validate, pack).",
5
5
  "license": "MIT",
6
6
  "author": "NeuroSquad",
@@ -39,13 +39,14 @@
39
39
  "./package.json": "./package.json"
40
40
  },
41
41
  "bin": {
42
- "neurosquad-card": "./dist/cli.js"
42
+ "neurosquad-card": "dist/cli.js"
43
43
  },
44
44
  "files": [
45
45
  "dist",
46
46
  "templates",
47
47
  "schema",
48
48
  "README.md",
49
+ "CHANGELOG.md",
49
50
  "LICENSE"
50
51
  ],
51
52
  "engines": {