@cleocode/contracts 2026.6.11 → 2026.6.13

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 (136) hide show
  1. package/dist/__tests__/operation-def.test.js +2 -1
  2. package/dist/__tests__/operation-def.test.js.map +1 -1
  3. package/dist/__tests__/sealed-credential.test.d.ts +19 -0
  4. package/dist/__tests__/sealed-credential.test.d.ts.map +1 -0
  5. package/dist/__tests__/sealed-credential.test.js +74 -0
  6. package/dist/__tests__/sealed-credential.test.js.map +1 -0
  7. package/dist/__tests__/skill-executor-contract.test.js +3 -0
  8. package/dist/__tests__/skill-executor-contract.test.js.map +1 -1
  9. package/dist/acceptance-gate.d.ts +2 -3
  10. package/dist/acceptance-gate.d.ts.map +1 -1
  11. package/dist/boundary.d.ts.map +1 -1
  12. package/dist/boundary.js +1 -19
  13. package/dist/boundary.js.map +1 -1
  14. package/dist/config.d.ts +102 -0
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/data-accessor.d.ts +21 -1
  17. package/dist/data-accessor.d.ts.map +1 -1
  18. package/dist/data-accessor.js.map +1 -1
  19. package/dist/dispatch/identity.d.ts +1 -1
  20. package/dist/dispatch/identity.d.ts.map +1 -1
  21. package/dist/dispatch/identity.js +5 -0
  22. package/dist/dispatch/identity.js.map +1 -1
  23. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  24. package/dist/dispatch/operations-registry.js +46 -168
  25. package/dist/dispatch/operations-registry.js.map +1 -1
  26. package/dist/docs/read.d.ts +2 -2
  27. package/dist/index.d.ts +12 -7
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +3 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/lease-ipc/__tests__/freeze.test.d.ts +15 -0
  32. package/dist/lease-ipc/__tests__/freeze.test.d.ts.map +1 -0
  33. package/dist/lease-ipc/__tests__/freeze.test.js +221 -0
  34. package/dist/lease-ipc/__tests__/freeze.test.js.map +1 -0
  35. package/dist/lease-ipc/index.d.ts +17 -0
  36. package/dist/lease-ipc/index.d.ts.map +1 -0
  37. package/dist/lease-ipc/index.js +16 -0
  38. package/dist/lease-ipc/index.js.map +1 -0
  39. package/dist/lease-ipc/messages.d.ts +754 -0
  40. package/dist/lease-ipc/messages.d.ts.map +1 -0
  41. package/dist/lease-ipc/messages.js +383 -0
  42. package/dist/lease-ipc/messages.js.map +1 -0
  43. package/dist/lease-ipc/version.d.ts +41 -0
  44. package/dist/lease-ipc/version.d.ts.map +1 -0
  45. package/dist/lease-ipc/version.js +43 -0
  46. package/dist/lease-ipc/version.js.map +1 -0
  47. package/dist/llm/interfaces.d.ts +23 -0
  48. package/dist/llm/interfaces.d.ts.map +1 -1
  49. package/dist/llm/sealed-credential.d.ts +164 -0
  50. package/dist/llm/sealed-credential.d.ts.map +1 -0
  51. package/dist/llm/sealed-credential.js +43 -0
  52. package/dist/llm/sealed-credential.js.map +1 -0
  53. package/dist/llm/system-of-use.d.ts +383 -0
  54. package/dist/llm/system-of-use.d.ts.map +1 -0
  55. package/dist/llm/system-of-use.js +238 -0
  56. package/dist/llm/system-of-use.js.map +1 -0
  57. package/dist/llm/system-resolver.d.ts +45 -11
  58. package/dist/llm/system-resolver.d.ts.map +1 -1
  59. package/dist/llm/system-resolver.js +14 -7
  60. package/dist/llm/system-resolver.js.map +1 -1
  61. package/dist/operations/__tests__/ensures-schema-registry.test.d.ts +17 -0
  62. package/dist/operations/__tests__/ensures-schema-registry.test.d.ts.map +1 -0
  63. package/dist/operations/__tests__/ensures-schema-registry.test.js +113 -0
  64. package/dist/operations/__tests__/ensures-schema-registry.test.js.map +1 -0
  65. package/dist/operations/ensures-schema-registry.d.ts +172 -0
  66. package/dist/operations/ensures-schema-registry.d.ts.map +1 -0
  67. package/dist/operations/ensures-schema-registry.js +226 -0
  68. package/dist/operations/ensures-schema-registry.js.map +1 -0
  69. package/dist/operations/llm.d.ts +101 -9
  70. package/dist/operations/llm.d.ts.map +1 -1
  71. package/dist/operations/validate.d.ts +1 -12
  72. package/dist/operations/validate.d.ts.map +1 -1
  73. package/dist/operations/validate.js +1 -1
  74. package/dist/playbook.d.ts +60 -0
  75. package/dist/playbook.d.ts.map +1 -1
  76. package/dist/session.d.ts +11 -0
  77. package/dist/session.d.ts.map +1 -1
  78. package/dist/session.js.map +1 -1
  79. package/dist/templates/manifest.d.ts +1 -1
  80. package/dist/tools/agent-tools.d.ts +231 -0
  81. package/dist/tools/agent-tools.d.ts.map +1 -0
  82. package/dist/tools/agent-tools.js +25 -0
  83. package/dist/tools/agent-tools.js.map +1 -0
  84. package/dist/tools/atomic.d.ts +19 -0
  85. package/dist/tools/atomic.d.ts.map +1 -1
  86. package/dist/tools/atomic.js +17 -0
  87. package/dist/tools/atomic.js.map +1 -1
  88. package/dist/tools/index.d.ts +2 -0
  89. package/dist/tools/index.d.ts.map +1 -1
  90. package/dist/tools/skill-executor.d.ts +7 -0
  91. package/dist/tools/skill-executor.d.ts.map +1 -1
  92. package/dist/tools/web-tools.d.ts +217 -0
  93. package/dist/tools/web-tools.d.ts.map +1 -0
  94. package/dist/tools/web-tools.js +31 -0
  95. package/dist/tools/web-tools.js.map +1 -0
  96. package/package.json +6 -2
  97. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  98. package/src/__tests__/operation-def.test.ts +2 -1
  99. package/src/__tests__/sealed-credential.test.ts +100 -0
  100. package/src/__tests__/skill-executor-contract.test.ts +3 -0
  101. package/src/acceptance-gate.ts +2 -3
  102. package/src/boundary.ts +1 -20
  103. package/src/config.ts +104 -0
  104. package/src/data-accessor.ts +22 -1
  105. package/src/dispatch/identity.ts +5 -0
  106. package/src/dispatch/operations-registry.ts +51 -170
  107. package/src/index.ts +103 -24
  108. package/src/lease-ipc/__tests__/freeze.test.ts +246 -0
  109. package/src/lease-ipc/index.ts +63 -0
  110. package/src/lease-ipc/messages.ts +471 -0
  111. package/src/lease-ipc/version.ts +46 -0
  112. package/src/llm/interfaces.ts +23 -0
  113. package/src/llm/sealed-credential.ts +166 -0
  114. package/src/llm/system-of-use.ts +503 -0
  115. package/src/llm/system-resolver.ts +46 -11
  116. package/src/operations/__tests__/ensures-schema-registry.test.ts +169 -0
  117. package/src/operations/ensures-schema-registry.ts +260 -0
  118. package/src/operations/llm.ts +105 -8
  119. package/src/operations/validate.ts +1 -14
  120. package/src/playbook.ts +61 -0
  121. package/src/session.ts +11 -0
  122. package/src/tools/agent-tools.ts +270 -0
  123. package/src/tools/atomic.ts +21 -0
  124. package/src/tools/index.ts +46 -0
  125. package/src/tools/skill-executor.ts +7 -0
  126. package/src/tools/web-tools.ts +247 -0
  127. package/dist/tessera.d.ts +0 -32
  128. package/dist/tessera.d.ts.map +0 -1
  129. package/dist/tessera.js +0 -11
  130. package/dist/tessera.js.map +0 -1
  131. package/dist/warp-chain.d.ts +0 -129
  132. package/dist/warp-chain.d.ts.map +0 -1
  133. package/dist/warp-chain.js +0 -13
  134. package/dist/warp-chain.js.map +0 -1
  135. package/src/tessera.ts +0 -35
  136. package/src/warp-chain.ts +0 -185
@@ -10,6 +10,8 @@
10
10
  */
11
11
 
12
12
  import type { BuiltinProviderId, ProviderId } from '../llm/provider-id.js';
13
+ import type { SealedCredential } from '../llm/sealed-credential.js';
14
+ import type { SystemOfUseKind, SystemOfUsePickerEntry } from '../llm/system-of-use.js';
13
15
 
14
16
  /**
15
17
  * @deprecated since Phase 4 (ADR-072). Use {@link ProviderId} from
@@ -196,6 +198,33 @@ export interface CredentialResultWire {
196
198
  authType: AuthTypeWire;
197
199
  }
198
200
 
201
+ /**
202
+ * Non-secret credential metadata — the E10 (T11753) redacted view of a resolved
203
+ * credential that {@link ResolvedLLM} carries inline.
204
+ *
205
+ * This is {@link CredentialResultWire} with the secret-bearing `apiKey` field
206
+ * **structurally removed**. The resolver returns this metadata so consumers can
207
+ * branch on `authType` / `source` / `provider` (e.g. choose OAuth-vs-api_key
208
+ * header style, surface `cleo llm whoami` diagnostics) WITHOUT any plaintext
209
+ * crossing the resolver boundary. The plaintext is reachable ONLY via
210
+ * {@link ResolvedLLM.sealedCredential}'s `fetch()` at the wire.
211
+ *
212
+ * @task T11753
213
+ * @epic T11746
214
+ */
215
+ export interface CredentialMetadataWire {
216
+ /** Provider transport this credential is for. */
217
+ provider: ModelTransport;
218
+ /**
219
+ * Which tier produced this credential. `undefined` only when no credential
220
+ * was resolved (in which case both this and {@link ResolvedLLM.sealedCredential}
221
+ * are `null`).
222
+ */
223
+ source: CredentialSourceWire | undefined;
224
+ /** Scheme used to present the credential to the provider. */
225
+ authType: AuthTypeWire;
226
+ }
227
+
199
228
  // ============================================================================
200
229
  // Role-based LLM resolution wire types (T9255 — Phase 2 T-llm-1)
201
230
  // ============================================================================
@@ -204,22 +233,28 @@ export interface CredentialResultWire {
204
233
  * Which configuration tier produced a {@link ResolvedLLM}.
205
234
  *
206
235
  * Resolution chain order:
207
- * 1. `role` — `config.llm.roles[role]` (explicit override)
208
- * 2. `profile` — `config.llm.profiles[roles[role].profile]`
209
- * 3. `default` — `config.llm.default` (canonical default)
210
- * 4. `default-profile` — `config.llm.profiles[config.llm.defaultProfile]`
211
- * 5. `implicit-fallback` — hard-coded fallback inside the resolver
236
+ * 1. `role` — `config.llm.roles[role]` (explicit override)
237
+ * 2. `profile` — `config.llm.profiles[roles[role].profile]`
238
+ * 3. `system` — `config.llm.systems[systemKey]` (granular override)
239
+ * 4. `default` — `config.llm.default` (canonical default)
240
+ * 5. `default-profile` — `config.llm.profiles[config.llm.defaultProfile]`
241
+ * 6. `registered-default` — `registerSystemOfUse()` default (below user config)
242
+ * 7. `implicit-fallback` — hard-coded fallback inside the resolver
212
243
  *
213
244
  * Useful for `cleo llm whoami` diagnostics.
214
245
  *
215
246
  * @task T9306
216
247
  * @task T11617 (`profile`, `default-profile`)
248
+ * @task T11748 (`system`)
249
+ * @task T11751 (`registered-default`)
217
250
  */
218
251
  export type ResolutionSource =
219
252
  | 'role'
220
253
  | 'profile'
254
+ | 'system'
221
255
  | 'default'
222
256
  | 'default-profile'
257
+ | 'registered-default'
223
258
  | 'implicit-fallback';
224
259
 
225
260
  /**
@@ -256,10 +291,33 @@ export interface ResolvedLLM {
256
291
  */
257
292
  client: unknown;
258
293
  /**
259
- * Resolved credential. `null` when no tier produced a token. Callers
260
- * MUST handle this case.
294
+ * Resolved credential **metadata** — provider / source / authType only.
295
+ * `null` when no tier produced a credential (in which case
296
+ * {@link sealedCredential} is also `null`). Callers MUST handle this case.
297
+ *
298
+ * E10 (T11753): the secret-bearing `apiKey` field has been **removed** — this
299
+ * is now a {@link CredentialMetadataWire}, NOT the old
300
+ * {@link CredentialResultWire}. The plaintext token never rides on this
301
+ * envelope; obtain it ONLY via {@link sealedCredential}'s `fetch()` at the
302
+ * wire. Branch on `authType` / `source` here for header-style / diagnostics.
303
+ */
304
+ credential: CredentialMetadataWire | null;
305
+ /**
306
+ * Sealed credential handle — the E10 on-demand-decrypt credential surface
307
+ * (T11752 · T11753 · T11746). `null` when no tier produced a credential
308
+ * (paired with `credential === null`); non-null exactly when a usable
309
+ * credential was resolved.
310
+ *
311
+ * The plaintext token is materialized ONLY by calling
312
+ * {@link SealedCredential.fetch} at the wire (`transportForProvider` /
313
+ * `session-factory.ts`) or daemon worker-injection — never returned up this
314
+ * envelope. This is the canonical credential surface; the old inline
315
+ * `credential.apiKey` plaintext is gone.
316
+ *
317
+ * @task T11752
318
+ * @task T11753
261
319
  */
262
- credential: CredentialResultWire | null;
320
+ sealedCredential: SealedCredential | null;
263
321
  /** Which config path produced this resolution. */
264
322
  source: ResolutionSource;
265
323
  /** When `roles[role].credentialLabel` was set, the label that was used. */
@@ -278,6 +336,19 @@ export interface ResolveLLMForRoleOptions {
278
336
  * Defaults to `process.cwd()`.
279
337
  */
280
338
  projectRoot?: string;
339
+ /**
340
+ * Optional encoded system-of-use key (e.g. `'sentient'`, `'tool:web-search'`)
341
+ * that activates the `llm.systems[key]` override tier in the resolution chain
342
+ * (E9 · T11748).
343
+ *
344
+ * When set, `llm.systems[systemKey]` is consulted AFTER any explicit
345
+ * role/argument override but BEFORE the global `llm.defaultProfile`. Threaded
346
+ * down by `resolveLLMForSystem`; direct role callers leave it unset and the
347
+ * system tier is skipped — preserving role-resolution behaviour unchanged.
348
+ *
349
+ * @task T11748
350
+ */
351
+ systemKey?: string;
281
352
  }
282
353
 
283
354
  // ============================================================================
@@ -570,6 +641,32 @@ export interface LlmWhoamiResult {
570
641
  entries: LlmWhoamiEntry[];
571
642
  }
572
643
 
644
+ /**
645
+ * Parameters for `llm.systems-of-use` (query — the profile-picker enumeration).
646
+ *
647
+ * Reserved for future filters (e.g. by {@link SystemOfUsePickerEntry.kind}).
648
+ *
649
+ * @task T11751
650
+ */
651
+ export interface LlmSystemsOfUseParams {
652
+ /** Optional kind filter — when set, only that axis is enumerated. */
653
+ kind?: SystemOfUseKind;
654
+ }
655
+
656
+ /**
657
+ * Result envelope for `llm.systems-of-use` (query · T11751 · AC2).
658
+ *
659
+ * The merged picker surface: every builtin {@link SystemOfUse} plus every
660
+ * runtime-registered system (`registerSystemOfUse`), so the TUI / Studio
661
+ * profile picker can enumerate them from ONE op.
662
+ *
663
+ * @task T11751
664
+ */
665
+ export interface LlmSystemsOfUseResult {
666
+ /** One entry per builtin or registered system-of-use. */
667
+ entries: SystemOfUsePickerEntry[];
668
+ }
669
+
573
670
  // ProviderProfile interface lives in ./llm/provider-profile.ts (T9262).
574
671
  // The generated catalog (packages/core/src/llm/generated/provider-profiles.ts)
575
672
  // imports the canonical type from the contracts package and emits each
@@ -2,11 +2,9 @@
2
2
  * Validate / Check Domain Operations
3
3
  *
4
4
  * T982 extension: Added new types for gate, archive, coherence, compliance-sync,
5
- * chain, grade, canon, workflow-compliance, verify-explain, and all protocol subtypes.
5
+ * grade, canon, workflow-compliance, verify-explain, and all protocol subtypes.
6
6
  */
7
7
 
8
- import type { WarpChain } from '../warp-chain.js';
9
-
10
8
  export type ValidationSeverity = 'error' | 'warning' | 'info';
11
9
 
12
10
  export interface ValidationViolation {
@@ -242,16 +240,6 @@ export interface ValidateArchiveStatsParams {
242
240
  }
243
241
  export type ValidateArchiveStatsResult = Record<string, unknown>;
244
242
 
245
- export interface ValidateChainParams {
246
- chain: WarpChain;
247
- }
248
- export interface ValidateChainResult {
249
- wellFormed: boolean;
250
- gateSatisfiable: boolean;
251
- errors: string[];
252
- warnings: string[];
253
- }
254
-
255
243
  export interface ValidateGradeParams {
256
244
  sessionId: string;
257
245
  }
@@ -379,7 +367,6 @@ export type CheckOps = {
379
367
  readonly 'gate.set': readonly [ValidateGateParams, ValidateGateResult];
380
368
  readonly 'verify.explain': readonly [ValidateVerifyExplainParams, ValidateVerifyExplainResult];
381
369
  readonly 'archive.stats': readonly [ValidateArchiveStatsParams, ValidateArchiveStatsResult];
382
- readonly 'chain.validate': readonly [ValidateChainParams, ValidateChainResult];
383
370
  readonly grade: readonly [ValidateGradeParams, ValidateGradeResult];
384
371
  readonly 'grade.list': readonly [ValidateGradeListParams, ValidateGradeListResult];
385
372
  readonly canon: readonly [ValidateCanonParams, ValidateCanonResult];
package/src/playbook.ts CHANGED
@@ -77,6 +77,55 @@ export interface PlaybookApprovalNode extends PlaybookNodeBase {
77
77
 
78
78
  export type PlaybookNode = PlaybookAgenticNode | PlaybookDeterministicNode | PlaybookApprovalNode;
79
79
 
80
+ /**
81
+ * Declarative, eval-free predicate over the playbook run `context` used to
82
+ * guard a conditional branch edge (T11806).
83
+ *
84
+ * The predicate is intentionally a *pure data* description — never an
85
+ * arbitrary expression — so the runtime stays deterministic and sandbox-safe.
86
+ * The named `field` is resolved against the accumulated run context
87
+ * (`context[field]`) and compared by exactly one of the optional operators
88
+ * below. When a `PlaybookEdge` carries a `when` predicate, the runtime only
89
+ * traverses that edge if the predicate evaluates to `true`.
90
+ *
91
+ * Exactly one comparison operator (`equals`, `notEquals`, `exists`, `in`, or
92
+ * `truthy`) should be supplied; the parser enforces this so an ambiguous
93
+ * predicate is rejected at parse time rather than mis-routing at runtime.
94
+ *
95
+ * @example
96
+ * ```yaml
97
+ * edges:
98
+ * - from: classify
99
+ * to: deep-research # taken when context.needsResearch === true
100
+ * when: { field: needsResearch, equals: true }
101
+ * - from: classify
102
+ * to: skip-research # taken when context.needsResearch !== true
103
+ * when: { field: needsResearch, notEquals: true }
104
+ * ```
105
+ *
106
+ * @task T11806 — cantbook runtime branching
107
+ */
108
+ export interface PlaybookEdgeCondition {
109
+ /** Context key whose value the predicate is evaluated against. */
110
+ field: string;
111
+ /** Edge is satisfied when `context[field]` strictly equals this value. */
112
+ equals?: unknown;
113
+ /** Edge is satisfied when `context[field]` strictly does NOT equal this value. */
114
+ notEquals?: unknown;
115
+ /**
116
+ * When `true`, edge is satisfied iff `field` is present in context;
117
+ * when `false`, satisfied iff `field` is absent from context.
118
+ */
119
+ exists?: boolean;
120
+ /** Edge is satisfied when `context[field]` is one of these values. */
121
+ in?: unknown[];
122
+ /**
123
+ * When `true`, edge is satisfied iff `context[field]` is truthy;
124
+ * when `false`, satisfied iff `context[field]` is falsy.
125
+ */
126
+ truthy?: boolean;
127
+ }
128
+
80
129
  export interface PlaybookEdge {
81
130
  from: string;
82
131
  to: string;
@@ -84,6 +133,18 @@ export interface PlaybookEdge {
84
133
  requires?: string[];
85
134
  ensures?: string[];
86
135
  };
136
+ /**
137
+ * Optional declarative branch guard (T11806). When present, the edge is
138
+ * only traversed if the {@link PlaybookEdgeCondition} predicate over the
139
+ * run `context` is satisfied. Edges without `when` are unconditional.
140
+ *
141
+ * A node may have multiple outgoing edges as long as every additional
142
+ * edge carries a `when` guard — the runtime evaluates them against the
143
+ * current context and routes to the single satisfied successor. More than
144
+ * one *unconditional* successor remains a fail-closed error (genuine
145
+ * ambiguity).
146
+ */
147
+ when?: PlaybookEdgeCondition;
87
148
  }
88
149
 
89
150
  export interface PlaybookErrorHandler {
package/src/session.ts CHANGED
@@ -121,6 +121,17 @@ export interface Session {
121
121
  previousSessionId?: string | null;
122
122
  /** ID of the session that follows this one. @defaultValue undefined */
123
123
  nextSessionId?: string | null;
124
+ /**
125
+ * ID of the fork-tree PARENT session that spawned this one (T11639).
126
+ *
127
+ * Sourced from `CLEO_PARENT_SESSION_ID` (stamped by the supervisor — T11629).
128
+ * Distinct from {@link Session.previousSessionId} (the linear resume chain):
129
+ * `parentSessionId` is the orchestrator→worker spawn edge. NULL/undefined for a
130
+ * root session with no spawning parent.
131
+ *
132
+ * @defaultValue undefined
133
+ */
134
+ parentSessionId?: string | null;
124
135
  /** Provider-specific agent identifier string. @defaultValue undefined */
125
136
  agentIdentifier?: string | null;
126
137
  /** ISO 8601 timestamp of when the handoff was consumed. @defaultValue undefined */
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Agent-facing tool family I/O contracts (T1741 · epic T11456 · SG-TOOLS).
3
+ *
4
+ * The shared, types-only I/O shapes for the six CORE agent-tool families wired
5
+ * into the {@link ./atomic.js | atomic} primitive layer and surfaced through the
6
+ * `AgentToolRegistry` (`packages/core/src/tools/agent-registry.ts`):
7
+ *
8
+ * - **terminal** — `run_shell` with PTY + non-PTY modes ({@link RunShellInput}).
9
+ * - **file/read** — `read_file` with pagination ({@link ReadFilePagedInput}).
10
+ * - **file/patch** — `apply_patch` with fuzzy hunk matching ({@link ApplyPatchInput}).
11
+ * - **search** — `search_files` over ripgrep ({@link SearchFilesInput}).
12
+ * - **git** — status / diff / log / commit ({@link GitStatusInput} …).
13
+ *
14
+ * These compose OVER the atomic `fs`/`shell`/`search` primitives — they never
15
+ * introduce a new side-effect surface. The implementations
16
+ * (`packages/core/src/tools/builtin/*`) perform ALL work through the injected
17
+ * `GuardedToolSurface` (deny-first chokepoint), so the same guardrail policy
18
+ * applies. Types-only — no runtime logic (Gate 10 `contracts-purity`).
19
+ *
20
+ * @epic T11456
21
+ * @task T1741
22
+ * @see ./atomic.js — the raw primitive I/O shapes these compose over
23
+ */
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // terminal — run_shell (PTY + non-PTY)
27
+ // ---------------------------------------------------------------------------
28
+
29
+ /**
30
+ * Execution mode for {@link RunShellInput}.
31
+ *
32
+ * - `pty` — allocate a pseudo-terminal (line-discipline, colour, interactive
33
+ * programs). Requires the OPTIONAL `node-pty` native dep; when it cannot be
34
+ * loaded the implementation transparently falls back to `spawn`.
35
+ * - `spawn` — plain `child_process.spawn` (no TTY). Always available.
36
+ * - `auto` — prefer `pty` when `node-pty` is loadable, else `spawn`.
37
+ */
38
+ export type ShellRunMode = 'pty' | 'spawn' | 'auto';
39
+
40
+ /** Input for the `run_shell` terminal tool. */
41
+ export interface RunShellInput {
42
+ /** Executable to run (argv form — NOT a shell string; no shell interpolation). */
43
+ readonly command: string;
44
+ /** Arguments for {@link RunShellInput.command}. */
45
+ readonly args?: readonly string[];
46
+ /** Working directory. */
47
+ readonly cwd?: string;
48
+ /** Extra environment variables (scrubbed at the guard chokepoint). */
49
+ readonly env?: Readonly<Record<string, string>>;
50
+ /** Hard timeout in milliseconds; the process is killed when exceeded. */
51
+ readonly timeoutMs?: number;
52
+ /** Execution mode. Defaults to `auto`. */
53
+ readonly mode?: ShellRunMode;
54
+ /** PTY column count (PTY mode only). Defaults to 80. */
55
+ readonly cols?: number;
56
+ /** PTY row count (PTY mode only). Defaults to 24. */
57
+ readonly rows?: number;
58
+ }
59
+
60
+ /** Result of the `run_shell` terminal tool. */
61
+ export interface RunShellResult {
62
+ /**
63
+ * Combined output. In `spawn` mode this is stdout; in `pty` mode stdout and
64
+ * stderr are interleaved on the single PTY stream (as a real terminal does).
65
+ */
66
+ readonly stdout: string;
67
+ /** Captured standard error. Empty in PTY mode (merged into {@link RunShellResult.stdout}). */
68
+ readonly stderr: string;
69
+ /** Process exit code (0 = success). `null` when killed by signal/timeout. */
70
+ readonly code: number | null;
71
+ /** The mode that actually ran (`pty` downgrades to `spawn` when unavailable). */
72
+ readonly mode: 'pty' | 'spawn';
73
+ /** Whether a PTY was requested but unavailable, so `spawn` was used instead. */
74
+ readonly ptyFellBack: boolean;
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // file — read_file (pagination)
79
+ // ---------------------------------------------------------------------------
80
+
81
+ /** Input for the paginated `read_file` tool. */
82
+ export interface ReadFilePagedInput {
83
+ /** Absolute path to read. */
84
+ readonly path: string;
85
+ /** 0-based line offset to start from. Defaults to 0. */
86
+ readonly offset?: number;
87
+ /** Maximum number of lines to return. Omit for "to end of file". */
88
+ readonly limit?: number;
89
+ }
90
+
91
+ /** Result of a paginated read. */
92
+ export interface ReadFilePagedResult {
93
+ /** Absolute path that was read. */
94
+ readonly path: string;
95
+ /** The selected slice of lines (joined with `\n`). */
96
+ readonly content: string;
97
+ /** 0-based line offset the slice began at. */
98
+ readonly offset: number;
99
+ /** Number of lines returned. */
100
+ readonly lineCount: number;
101
+ /** Total number of lines in the file. */
102
+ readonly totalLines: number;
103
+ /** Whether more lines exist after the returned slice. */
104
+ readonly hasMore: boolean;
105
+ }
106
+
107
+ // ---------------------------------------------------------------------------
108
+ // file — apply_patch (fuzzy)
109
+ // ---------------------------------------------------------------------------
110
+
111
+ /** Input for the `apply_patch` tool (fuzzy, whitespace-tolerant replacement). */
112
+ export interface ApplyPatchInput {
113
+ /** Absolute path of the file to patch. */
114
+ readonly path: string;
115
+ /** Exact-or-fuzzy block of original text to locate. */
116
+ readonly oldText: string;
117
+ /** Replacement text. */
118
+ readonly newText: string;
119
+ /**
120
+ * Permit fuzzy (leading/trailing whitespace-insensitive) matching when an
121
+ * exact match is not found. Defaults to `true`.
122
+ */
123
+ readonly fuzzy?: boolean;
124
+ }
125
+
126
+ /** Result of an `apply_patch`. */
127
+ export interface ApplyPatchResult {
128
+ /** Absolute path that was patched. */
129
+ readonly path: string;
130
+ /** Whether the replacement was applied. */
131
+ readonly applied: boolean;
132
+ /** How the match was located: exact, fuzzy (whitespace-normalised), or none. */
133
+ readonly matchKind: 'exact' | 'fuzzy' | 'none';
134
+ /** 0-based line where the replacement began (when applied). */
135
+ readonly startLine?: number;
136
+ }
137
+
138
+ // ---------------------------------------------------------------------------
139
+ // search — search_files (ripgrep)
140
+ // ---------------------------------------------------------------------------
141
+
142
+ /** Input for the ripgrep-backed `search_files` tool. */
143
+ export interface SearchFilesInput {
144
+ /** Pattern to search for (regex by default — see {@link SearchFilesInput.fixedStrings}). */
145
+ readonly pattern: string;
146
+ /** Root directory to search under. */
147
+ readonly root: string;
148
+ /** Treat {@link SearchFilesInput.pattern} as a literal string, not a regex. */
149
+ readonly fixedStrings?: boolean;
150
+ /** Case-insensitive matching. */
151
+ readonly ignoreCase?: boolean;
152
+ /** Restrict to files matching this ripgrep glob (e.g. `*.ts`). */
153
+ readonly glob?: string;
154
+ /** Cap on returned matches. Defaults to 1000. */
155
+ readonly maxResults?: number;
156
+ }
157
+
158
+ /** A single `search_files` match. */
159
+ export interface SearchFilesMatch {
160
+ /** Absolute (or root-relative) path of the matching file. */
161
+ readonly path: string;
162
+ /** 1-based line number of the match. */
163
+ readonly line: number;
164
+ /** The matching line's text (trailing newline stripped). */
165
+ readonly text: string;
166
+ }
167
+
168
+ /** Result of `search_files`. */
169
+ export interface SearchFilesResult {
170
+ /** Matches in ripgrep order. */
171
+ readonly matches: readonly SearchFilesMatch[];
172
+ /** Whether {@link SearchFilesInput.maxResults} truncated the results. */
173
+ readonly truncated: boolean;
174
+ /**
175
+ * `true` when ripgrep (`rg`) was unavailable and the search degraded to a
176
+ * built-in scan, so callers can surface reduced fidelity.
177
+ */
178
+ readonly degraded: boolean;
179
+ }
180
+
181
+ // ---------------------------------------------------------------------------
182
+ // git — status / diff / log / commit
183
+ // ---------------------------------------------------------------------------
184
+
185
+ /** Common options for the git tools. */
186
+ export interface GitToolBase {
187
+ /** Repository working directory. Defaults to `process.cwd()`. */
188
+ readonly cwd?: string;
189
+ /** Hard timeout in milliseconds. */
190
+ readonly timeoutMs?: number;
191
+ }
192
+
193
+ /** Input for `git_status`. */
194
+ export interface GitStatusInput extends GitToolBase {}
195
+
196
+ /** One porcelain-v1 status entry. */
197
+ export interface GitStatusEntry {
198
+ /** Two-char XY porcelain status code (e.g. ` M`, `A `, `??`). */
199
+ readonly status: string;
200
+ /** Path the status applies to. */
201
+ readonly path: string;
202
+ }
203
+
204
+ /** Result of `git_status`. */
205
+ export interface GitStatusResult {
206
+ /** The current branch name (`HEAD` when detached/unknown). */
207
+ readonly branch: string;
208
+ /** Parsed porcelain entries. */
209
+ readonly entries: readonly GitStatusEntry[];
210
+ /** Whether the working tree has any uncommitted changes. */
211
+ readonly clean: boolean;
212
+ }
213
+
214
+ /** Input for `git_diff`. */
215
+ export interface GitDiffInput extends GitToolBase {
216
+ /** Show staged (`--cached`) changes instead of the working tree. */
217
+ readonly staged?: boolean;
218
+ /** Restrict the diff to these paths. */
219
+ readonly paths?: readonly string[];
220
+ }
221
+
222
+ /** Result of `git_diff`. */
223
+ export interface GitDiffResult {
224
+ /** The unified diff text (empty when there are no changes). */
225
+ readonly diff: string;
226
+ }
227
+
228
+ /** Input for `git_log`. */
229
+ export interface GitLogInput extends GitToolBase {
230
+ /** Maximum number of commits to return. Defaults to 20. */
231
+ readonly maxCount?: number;
232
+ }
233
+
234
+ /** One commit in a `git_log` result. */
235
+ export interface GitLogEntry {
236
+ /** Full commit SHA. */
237
+ readonly sha: string;
238
+ /** Author name. */
239
+ readonly author: string;
240
+ /** ISO-8601 author date. */
241
+ readonly date: string;
242
+ /** Commit subject (first line). */
243
+ readonly subject: string;
244
+ }
245
+
246
+ /** Result of `git_log`. */
247
+ export interface GitLogResult {
248
+ /** Commits newest-first. */
249
+ readonly commits: readonly GitLogEntry[];
250
+ }
251
+
252
+ /** Input for `git_commit`. */
253
+ export interface GitCommitInput extends GitToolBase {
254
+ /** Commit message. */
255
+ readonly message: string;
256
+ /** Stage all tracked modifications first (`-a`). Defaults to `false`. */
257
+ readonly all?: boolean;
258
+ /** Explicit paths to stage before committing. */
259
+ readonly paths?: readonly string[];
260
+ }
261
+
262
+ /** Result of `git_commit`. */
263
+ export interface GitCommitResult {
264
+ /** Whether the commit was created. */
265
+ readonly committed: boolean;
266
+ /** The new commit SHA (when created). */
267
+ readonly sha?: string;
268
+ /** Git's stdout/stderr summary (e.g. "nothing to commit"). */
269
+ readonly summary: string;
270
+ }
@@ -36,6 +36,27 @@ export const TOOL_CLASSES = ['fs', 'shell', 'search', 'net', 'notebook'] as cons
36
36
  /** One of the canonical {@link TOOL_CLASSES}. */
37
37
  export type ToolClass = (typeof TOOL_CLASSES)[number];
38
38
 
39
+ /**
40
+ * Agent-facing **toolset** groups (T1739 · AC4 · epic T11456).
41
+ *
42
+ * Where {@link TOOL_CLASSES} groups primitives by their SIDE-EFFECT surface (and
43
+ * therefore by guardrail policy), a {@link Toolset} groups tools by the
44
+ * AGENT-FACING CAPABILITY a model selects when reasoning — the dimension Hermes'
45
+ * `tools/registry.py` exposes to the loop. The two axes are orthogonal: an `fs`
46
+ * class primitive belongs to the `file` toolset, a `shell` class primitive to the
47
+ * `terminal` toolset, a `net` class primitive to the `web` toolset, etc.
48
+ *
49
+ * - `terminal` — shell / process execution (`executeShell`, `runGit`).
50
+ * - `file` — filesystem read/write/search (`readFileText`, `writeFileAtomic`, …).
51
+ * - `web` — outbound network / fetch (`fetch`).
52
+ * - `agent` — sub-agent / delegation / task orchestration tools.
53
+ * - `media` — image / audio / notebook authoring (`notebookEdit`, …).
54
+ */
55
+ export const AGENT_TOOLSETS = ['terminal', 'file', 'web', 'agent', 'media'] as const;
56
+
57
+ /** One of the canonical {@link AGENT_TOOLSETS}. */
58
+ export type Toolset = (typeof AGENT_TOOLSETS)[number];
59
+
39
60
  /**
40
61
  * Descriptor for a single registered tool primitive. The registry of these is
41
62
  * the SoT for "what atomic tools exist", consumed by the guardrail chokepoint
@@ -7,6 +7,30 @@
7
7
  * @epic T9835
8
8
  */
9
9
 
10
+ // === Agent-facing tool families (T1741 / T11456) ===
11
+ export type {
12
+ ApplyPatchInput,
13
+ ApplyPatchResult,
14
+ GitCommitInput,
15
+ GitCommitResult,
16
+ GitDiffInput,
17
+ GitDiffResult,
18
+ GitLogEntry,
19
+ GitLogInput,
20
+ GitLogResult,
21
+ GitStatusEntry,
22
+ GitStatusInput,
23
+ GitStatusResult,
24
+ GitToolBase,
25
+ ReadFilePagedInput,
26
+ ReadFilePagedResult,
27
+ RunShellInput,
28
+ RunShellResult,
29
+ SearchFilesInput,
30
+ SearchFilesMatch,
31
+ SearchFilesResult,
32
+ ShellRunMode,
33
+ } from './agent-tools.js';
10
34
  export type { FetchBrainEntriesInput, FetchBrainEntriesOutput } from './brain-fetch.js';
11
35
  export type { ObserveBrainInput, ObserveBrainOutput } from './brain-observe.js';
12
36
  // === BrainTools (T10070 / T9835c) ===
@@ -41,3 +65,25 @@ export type {
41
65
  ScoreTaskInput,
42
66
  ScoreTaskResult,
43
67
  } from './score-task-priority.js';
68
+ // === Web + browser agent tools (T1742 / T11456) ===
69
+ export type {
70
+ AccessibilityNode,
71
+ BrowserActionResult,
72
+ BrowserClickInput,
73
+ BrowserNavigateInput,
74
+ BrowserPageState,
75
+ BrowserPressInput,
76
+ BrowserScrollDirection,
77
+ BrowserScrollInput,
78
+ BrowserScrollResult,
79
+ BrowserSnapshotResult,
80
+ BrowserTypeInput,
81
+ BrowserVisionInput,
82
+ BrowserVisionResult,
83
+ WebExtractInput,
84
+ WebExtractResult,
85
+ WebSearchBackendId,
86
+ WebSearchHit,
87
+ WebSearchInput,
88
+ WebSearchResult,
89
+ } from './web-tools.js';
@@ -44,6 +44,7 @@
44
44
  * @see ./atomic.js — the tool I/O shapes this seam composes over
45
45
  */
46
46
 
47
+ import type { RunShellInput, RunShellResult } from './agent-tools.js';
47
48
  import type {
48
49
  ExecuteShellInput,
49
50
  ExecuteShellResult,
@@ -86,6 +87,12 @@ export interface GuardedToolSurface {
86
87
  pathExists(input: PathExistsInput): Promise<PathExistsResult>;
87
88
  /** Run a command with explicit cwd/env/timeout through the guard. */
88
89
  executeShell(input: ExecuteShellInput): Promise<ExecuteShellResult>;
90
+ /**
91
+ * Run a command under a pseudo-terminal (PTY) through the guard, falling back
92
+ * to a non-PTY spawn when a PTY backend is unavailable. Subject to the same
93
+ * deny-first command policy + env scrubbing as {@link GuardedToolSurface.executeShell}.
94
+ */
95
+ executePty(input: RunShellInput): Promise<RunShellResult>;
89
96
  /** Run a git subcommand through the guard. */
90
97
  runGit(input: RunGitInput): Promise<ExecuteShellResult>;
91
98
  }