@botbotgo/agent-harness 0.0.155 → 0.0.157

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.
@@ -96,6 +96,24 @@ export type RuntimeSnapshotTracing = {
96
96
  tags?: string[];
97
97
  metadata?: Record<string, unknown>;
98
98
  };
99
+ export type RuntimeGovernanceRiskLevel = "low" | "medium" | "high";
100
+ export type RuntimeGovernanceToolPolicy = {
101
+ toolName: string;
102
+ toolId: string;
103
+ toolType: string;
104
+ category: "local" | "backend" | "mcp" | "provider-native";
105
+ risk: RuntimeGovernanceRiskLevel;
106
+ requiresApproval: boolean;
107
+ approvalPolicy: "explicit-hitl" | "runtime-default" | "none";
108
+ hasInputSchema: boolean;
109
+ inputRiskHints: string[];
110
+ };
111
+ export type RuntimeGovernanceBundle = {
112
+ bundleId: string;
113
+ title: string;
114
+ summary: string;
115
+ toolPolicies: RuntimeGovernanceToolPolicy[];
116
+ };
99
117
  export type RuntimeSnapshot = {
100
118
  agentId: string;
101
119
  model?: RuntimeSnapshotModel;
@@ -103,6 +121,9 @@ export type RuntimeSnapshot = {
103
121
  skills: RuntimeSnapshotSkill[];
104
122
  memory: string[];
105
123
  tracing?: RuntimeSnapshotTracing;
124
+ governance?: {
125
+ bundles: RuntimeGovernanceBundle[];
126
+ };
106
127
  };
107
128
  export type MemoryCandidate = {
108
129
  content: string;
@@ -195,6 +216,37 @@ export type RecallInput = {
195
216
  export type RecallResult = {
196
217
  items: MemoryRecord[];
197
218
  };
219
+ export type ListMemoriesInput = {
220
+ scopes?: MemoryScope[];
221
+ kinds?: MemoryKind[];
222
+ status?: MemoryRecordStatus[];
223
+ threadId?: string;
224
+ agentId?: string;
225
+ workspaceId?: string;
226
+ userId?: string;
227
+ projectId?: string;
228
+ limit?: number;
229
+ };
230
+ export type ListMemoriesResult = {
231
+ items: MemoryRecord[];
232
+ };
233
+ export type UpdateMemoryInput = {
234
+ memoryId: string;
235
+ content?: string;
236
+ summary?: string;
237
+ status?: MemoryRecordStatus;
238
+ confidence?: number;
239
+ expiresAt?: string | null;
240
+ sourceType?: string;
241
+ sourceRefs?: string[];
242
+ tags?: string[];
243
+ observedAt?: string;
244
+ lastConfirmedAt?: string;
245
+ provenance?: Record<string, unknown>;
246
+ };
247
+ export type RemoveMemoryInput = {
248
+ memoryId: string;
249
+ };
198
250
  /**
199
251
  * Operator-facing projection of tool execution policy already compiled into a binding.
200
252
  * This summarizes existing timeout, retry, validation, and retry-safety hints without
@@ -508,6 +560,7 @@ export type SkillPackagingConvention = {
508
560
  export type PolicyDecision = {
509
561
  allowed: boolean;
510
562
  reasons: string[];
563
+ bundles?: RuntimeGovernanceBundle[];
511
564
  };
512
565
  export type PolicyEvaluator = {
513
566
  kind: string;
@@ -517,6 +570,32 @@ export type EventSubscriber = {
517
570
  kind: string;
518
571
  onEvent: HarnessEventListener;
519
572
  };
573
+ export type RuntimeEvaluationExportInput = {
574
+ sessionId: string;
575
+ requestId: string;
576
+ includeArtifacts?: boolean;
577
+ includeArtifactContents?: boolean;
578
+ expectedOutput?: string;
579
+ rubric?: string[];
580
+ tags?: string[];
581
+ metadata?: Record<string, unknown>;
582
+ };
583
+ export type RuntimeEvaluationArtifact = ArtifactRecord & {
584
+ content?: unknown;
585
+ };
586
+ export type RuntimeEvaluationExport = {
587
+ session: SessionRecord | null;
588
+ request: RequestRecord | null;
589
+ approvals: ApprovalRecord[];
590
+ transcript: TranscriptMessage[];
591
+ events: HarnessEvent[];
592
+ artifacts: RuntimeEvaluationArtifact[];
593
+ runtimeHealth: RuntimeHealthSnapshot;
594
+ expectedOutput?: string;
595
+ rubric: string[];
596
+ tags: string[];
597
+ metadata?: Record<string, unknown>;
598
+ };
520
599
  export type RuntimeInventoryContext = {
521
600
  workspace: WorkspaceBundle;
522
601
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- export { AgentHarnessRuntime, cancelRun, createAgentHarness, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, getAgent, getApproval, getRequest, getHealth, getSession, listAgentSkills, listApprovals, listRequests, listSessions, memorize, normalizeUserChatInput, recall, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
2
- export type { MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, NormalizeUserChatInputOptions, RecallInput, RecallResult, UserChatInput, UserChatMessage, } from "./api.js";
1
+ export { AgentHarnessAcpServer, AgentHarnessRuntime, cancelRun, createAgentHarness, createAcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, getArtifact, getAgent, getApproval, getRequest, getHealth, listMemories, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listSessions, memorize, normalizeUserChatInput, recall, removeMemory, resolveApproval, run, serveToolsOverStdio, subscribe, stop, updateMemory, } from "./api.js";
2
+ export type { AcpApproval, AcpArtifact, AcpEventNotification, AcpJsonRpcError, AcpJsonRpcRequest, AcpJsonRpcResponse, AcpJsonRpcSuccess, AcpRequestRecord, AcpRunRequestParams, AcpServerCapabilities, AcpSessionRecord, } from "./acp.js";
3
+ export type { ListMemoriesInput, ListMemoriesResult, MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, NormalizeUserChatInputOptions, RecallInput, RecallResult, RemoveMemoryInput, RuntimeEvaluationExport, RuntimeEvaluationExportInput, UpdateMemoryInput, UserChatInput, UserChatMessage, } from "./api.js";
3
4
  export type { ToolMcpServerOptions } from "./mcp.js";
4
5
  export { tool } from "./tools.js";
5
6
  export type { UpstreamTimelineProjection, UpstreamTimelineReducer } from "./upstream-events.js";
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { AgentHarnessRuntime, cancelRun, createAgentHarness, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, getAgent, getApproval, getRequest, getHealth, getSession, listAgentSkills, listApprovals, listRequests, listSessions, memorize, normalizeUserChatInput, recall, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
1
+ export { AgentHarnessAcpServer, AgentHarnessRuntime, cancelRun, createAgentHarness, createAcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, getArtifact, getAgent, getApproval, getRequest, getHealth, listMemories, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listSessions, memorize, normalizeUserChatInput, recall, removeMemory, resolveApproval, run, serveToolsOverStdio, subscribe, stop, updateMemory, } from "./api.js";
2
2
  export { tool } from "./tools.js";
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.154";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.156";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.154";
1
+ export const AGENT_HARNESS_VERSION = "0.0.156";
@@ -69,6 +69,7 @@ export declare class FilePersistence implements RuntimePersistence {
69
69
  resolveApproval(threadId: string, runId: string, approvalId: string, status: InternalApprovalRecord["status"]): Promise<InternalApprovalRecord>;
70
70
  createArtifact(threadId: string, runId: string, artifact: ArtifactRecord, content: unknown): Promise<ArtifactRecord>;
71
71
  listArtifacts(threadId: string, runId: string): Promise<ArtifactListing>;
72
+ readArtifact(threadId: string, runId: string, artifactPath: string): Promise<unknown>;
72
73
  appendThreadMessage(threadId: string, message: TranscriptMessage): Promise<void>;
73
74
  listThreadMessages(threadId: string, limit?: number): Promise<TranscriptMessage[]>;
74
75
  saveRecoveryIntent(threadId: string, runId: string, intent: RecoveryIntent): Promise<void>;
@@ -328,7 +328,9 @@ export class FilePersistence {
328
328
  if (!(await fileExists(eventsDir))) {
329
329
  return [];
330
330
  }
331
- const entries = (await readdir(eventsDir)).sort();
331
+ const entries = (await readdir(eventsDir))
332
+ .filter((entry) => entry.endsWith(".json"))
333
+ .sort((left, right) => left.localeCompare(right));
332
334
  return Promise.all(entries.map((entry) => readJson(path.join(eventsDir, entry))));
333
335
  }
334
336
  async listApprovals(filter = {}) {
@@ -460,6 +462,13 @@ export class FilePersistence {
460
462
  async listArtifacts(threadId, runId) {
461
463
  return readJson(path.join(this.runDir(threadId, runId), "artifacts.json"));
462
464
  }
465
+ async readArtifact(threadId, runId, artifactPath) {
466
+ const filePath = path.join(this.runDir(threadId, runId), artifactPath);
467
+ if (!(await fileExists(filePath))) {
468
+ return null;
469
+ }
470
+ return readJson(filePath);
471
+ }
463
472
  async appendThreadMessage(threadId, message) {
464
473
  const messagesPath = path.join(this.threadDir(threadId), "messages.json");
465
474
  const current = (await fileExists(messagesPath))
@@ -120,6 +120,7 @@ export interface RuntimePersistence {
120
120
  }): Promise<void>;
121
121
  setRunState(threadId: string, runId: string, state: RunState, checkpointRef?: string | null): Promise<void>;
122
122
  appendEvent(event: HarnessEvent): Promise<void>;
123
+ listRunEvents(threadId: string, runId: string): Promise<HarnessEvent[]>;
123
124
  listSessions(filter?: ThreadSummaryFilter): Promise<ThreadSummary[]>;
124
125
  listRuns(filter?: RunSummaryFilter): Promise<RunSummary[]>;
125
126
  getRun(runId: string): Promise<RunSummary | null>;
@@ -147,6 +148,7 @@ export interface RuntimePersistence {
147
148
  resolveApproval(threadId: string, runId: string, approvalId: string, status: InternalApprovalRecord["status"]): Promise<InternalApprovalRecord>;
148
149
  createArtifact(threadId: string, runId: string, artifact: ArtifactRecord, content: unknown): Promise<ArtifactRecord>;
149
150
  listArtifacts(threadId: string, runId: string): Promise<ArtifactListing>;
151
+ readArtifact(threadId: string, runId: string, artifactPath: string): Promise<unknown>;
150
152
  appendThreadMessage(threadId: string, message: TranscriptMessage): Promise<void>;
151
153
  listThreadMessages(threadId: string, limit?: number): Promise<TranscriptMessage[]>;
152
154
  saveRecoveryIntent(threadId: string, runId: string, intent: RecoveryIntent): Promise<void>;
@@ -63,6 +63,7 @@ export declare class AgentRuntimeAdapter {
63
63
  private resolveTools;
64
64
  private getToolNameMapping;
65
65
  private resolveFilesystemBackend;
66
+ private resolveFilesystemRootDir;
66
67
  private resolveBuiltinMiddlewareBackend;
67
68
  private createDeclaredMiddlewareResolverOptions;
68
69
  private createAssemblyResolvers;
@@ -75,7 +76,10 @@ export declare class AgentRuntimeAdapter {
75
76
  private createLangChainRunnable;
76
77
  private createRunnable;
77
78
  private createDeepAgentRunnable;
78
- create(binding: CompiledAgentBinding): Promise<RunnableLike>;
79
+ private buildRunnableCacheKey;
80
+ create(binding: CompiledAgentBinding, options?: {
81
+ threadId?: string;
82
+ }): Promise<RunnableLike>;
79
83
  invoke(binding: CompiledAgentBinding, input: MessageContent, threadId: string, runId: string, resumePayload?: unknown, history?: TranscriptMessage[], options?: {
80
84
  context?: Record<string, unknown>;
81
85
  state?: Record<string, unknown>;
@@ -159,7 +159,7 @@ function shouldAttachMinimalDeepAgentBackend(binding) {
159
159
  export class AgentRuntimeAdapter {
160
160
  options;
161
161
  modelCache = new Map();
162
- runnableCache = new WeakMap();
162
+ runnableCache = new Map();
163
163
  toolNameMappingCache = new WeakMap();
164
164
  constructor(options = {}) {
165
165
  this.options = options;
@@ -213,7 +213,12 @@ export class AgentRuntimeAdapter {
213
213
  }
214
214
  }
215
215
  invalidateBindingRuntimeCaches(binding) {
216
- this.runnableCache.delete(binding);
216
+ const prefix = `${binding.agent.sourcePath}::`;
217
+ for (const key of Array.from(this.runnableCache.keys())) {
218
+ if (key.startsWith(prefix)) {
219
+ this.runnableCache.delete(key);
220
+ }
221
+ }
217
222
  this.modelCache.clear();
218
223
  }
219
224
  resolveTools(tools, binding) {
@@ -232,15 +237,19 @@ export class AgentRuntimeAdapter {
232
237
  this.toolNameMappingCache.set(binding, resolved);
233
238
  return resolved;
234
239
  }
235
- resolveFilesystemBackend(binding) {
240
+ resolveFilesystemBackend(binding, options = {}) {
236
241
  const filesystemConfig = getBindingFilesystemConfig(binding);
242
+ const sessionStorage = typeof filesystemConfig?.sessionStorage === "object" && filesystemConfig.sessionStorage
243
+ ? filesystemConfig.sessionStorage
244
+ : undefined;
237
245
  const configuredRootDir = typeof filesystemConfig?.rootDir === "string" && filesystemConfig.rootDir.trim().length > 0
238
246
  ? filesystemConfig.rootDir
239
247
  : undefined;
240
248
  const workspaceRoot = binding.harnessRuntime.workspaceRoot;
241
- const rootDir = configuredRootDir
249
+ const baseRootDir = configuredRootDir
242
250
  ? (path.isAbsolute(configuredRootDir) ? configuredRootDir : path.resolve(workspaceRoot ?? process.cwd(), configuredRootDir))
243
251
  : workspaceRoot ?? process.cwd();
252
+ const rootDir = this.resolveFilesystemRootDir(baseRootDir, binding, sessionStorage, options.threadId);
244
253
  return new FilesystemBackend({
245
254
  rootDir,
246
255
  virtualMode: filesystemConfig?.virtualMode === true,
@@ -249,6 +258,25 @@ export class AgentRuntimeAdapter {
249
258
  : 10,
250
259
  });
251
260
  }
261
+ resolveFilesystemRootDir(baseRootDir, binding, sessionStorage, threadId) {
262
+ const enabled = sessionStorage?.enabled === true;
263
+ if (!enabled || !threadId) {
264
+ return baseRootDir;
265
+ }
266
+ const workspaceRoot = binding.harnessRuntime.workspaceRoot ?? process.cwd();
267
+ const runRoot = binding.harnessRuntime.runRoot;
268
+ const configuredRootDir = typeof sessionStorage.rootDir === "string" && sessionStorage.rootDir.trim().length > 0
269
+ ? sessionStorage.rootDir.trim()
270
+ : "{runRoot}/threads/{threadId}/filesystem";
271
+ const rendered = configuredRootDir
272
+ .replaceAll("{threadId}", threadId)
273
+ .replaceAll("{sessionId}", threadId)
274
+ .replaceAll("{agentId}", binding.agent.id)
275
+ .replaceAll("{runRoot}", runRoot)
276
+ .replaceAll("{workspaceRoot}", workspaceRoot)
277
+ .replaceAll("{baseRootDir}", baseRootDir);
278
+ return path.isAbsolute(rendered) ? rendered : path.resolve(workspaceRoot, rendered);
279
+ }
252
280
  resolveBuiltinMiddlewareBackend(binding, options = {}) {
253
281
  return resolveBuiltinMiddlewareBackendHelper({
254
282
  binding,
@@ -257,7 +285,7 @@ export class AgentRuntimeAdapter {
257
285
  options,
258
286
  });
259
287
  }
260
- createDeclaredMiddlewareResolverOptions(binding) {
288
+ createDeclaredMiddlewareResolverOptions(binding, options = {}) {
261
289
  return {
262
290
  resolveModel: (model) => this.resolveModel(model),
263
291
  resolveBackend: (resolvedBinding) => {
@@ -266,7 +294,7 @@ export class AgentRuntimeAdapter {
266
294
  },
267
295
  resolveFilesystemBackend: (resolvedBinding) => {
268
296
  const targetBinding = resolvedBinding ?? binding;
269
- return targetBinding ? this.resolveFilesystemBackend(targetBinding) : undefined;
297
+ return targetBinding ? this.resolveFilesystemBackend(targetBinding, { threadId: options.threadId }) : undefined;
270
298
  },
271
299
  resolveCustom: this.options.declaredMiddlewareResolver,
272
300
  binding,
@@ -276,8 +304,8 @@ export class AgentRuntimeAdapter {
276
304
  return {
277
305
  resolveModel: (model) => this.resolveModel(model),
278
306
  resolveTools: (tools, currentBinding) => this.resolveTools(tools, currentBinding),
279
- resolveFilesystemBackend: (currentBinding) => this.resolveFilesystemBackend(currentBinding),
280
- createDeclaredMiddlewareResolverOptions: (currentBinding) => this.createDeclaredMiddlewareResolverOptions(currentBinding),
307
+ resolveFilesystemBackend: (currentBinding) => this.resolveFilesystemBackend(currentBinding, { threadId: options.threadId }),
308
+ createDeclaredMiddlewareResolverOptions: (currentBinding) => this.createDeclaredMiddlewareResolverOptions(currentBinding, options),
281
309
  resolveBuiltinMiddlewareBackend: (currentBinding, currentOptions = {}) => this.resolveBuiltinMiddlewareBackend(currentBinding, currentOptions),
282
310
  resolveSubagents: (subagents, currentBinding) => this.resolveSubagents(subagents, currentBinding),
283
311
  invokeBuiltinTaskTool: (currentBinding, toolInput, currentOptions = {}) => this.invokeBuiltinTaskTool(currentBinding, toolInput, currentOptions),
@@ -313,8 +341,8 @@ export class AgentRuntimeAdapter {
313
341
  createDeclaredMiddlewareResolverOptions: assembly.createDeclaredMiddlewareResolverOptions,
314
342
  });
315
343
  }
316
- async resolveLangChainRuntimeExtensionMiddleware(binding) {
317
- const assembly = this.createAssemblyResolvers(binding);
344
+ async resolveLangChainRuntimeExtensionMiddleware(binding, options = {}) {
345
+ const assembly = this.createAssemblyResolvers(binding, options);
318
346
  return resolveLangChainRuntimeExtensionMiddlewareHelper({
319
347
  binding,
320
348
  materializeAutomaticSummarizationMiddleware: (currentBinding) => this.materializeAutomaticSummarizationMiddleware(currentBinding),
@@ -324,14 +352,14 @@ export class AgentRuntimeAdapter {
324
352
  resolveSubagents: assembly.resolveSubagents,
325
353
  });
326
354
  }
327
- async resolveMiddleware(binding, interruptOn) {
328
- const assembly = this.createAssemblyResolvers(binding);
355
+ async resolveMiddleware(binding, interruptOn, options = {}) {
356
+ const assembly = this.createAssemblyResolvers(binding, options);
329
357
  return resolveMiddlewareHelper({
330
358
  binding,
331
359
  interruptOn,
332
360
  runtimeAdapterOptions: this.options,
333
361
  createDeclaredMiddlewareResolverOptions: assembly.createDeclaredMiddlewareResolverOptions,
334
- resolveLangChainRuntimeExtensionMiddleware: (currentBinding) => this.resolveLangChainRuntimeExtensionMiddleware(currentBinding),
362
+ resolveLangChainRuntimeExtensionMiddleware: (currentBinding) => this.resolveLangChainRuntimeExtensionMiddleware(currentBinding, options),
335
363
  });
336
364
  }
337
365
  async resolveSubagents(subagents, binding) {
@@ -354,7 +382,7 @@ export class AgentRuntimeAdapter {
354
382
  const interruptOn = resolveRunnableInterruptOn(binding);
355
383
  const resolvedModel = await this.resolveModel(primaryModel);
356
384
  const resolvedTools = this.resolveTools(primaryTools, binding);
357
- const resolvedMiddleware = await this.resolveMiddleware(binding, interruptOn);
385
+ const resolvedMiddleware = await this.resolveMiddleware(binding, interruptOn, { threadId: options.threadId });
358
386
  const resolvedCheckpointer = resolveRunnableCheckpointer(this.options, binding);
359
387
  const resolvedStore = this.options.storeResolver?.(binding);
360
388
  const model = resolvedModel;
@@ -372,12 +400,12 @@ export class AgentRuntimeAdapter {
372
400
  systemPromptOverride: options.systemPromptOverride,
373
401
  }));
374
402
  }
375
- async createRunnable(binding) {
403
+ async createRunnable(binding, options = {}) {
376
404
  if (getBindingAdapterKind(binding) === "langgraph") {
377
405
  throw new Error(`Agent ${binding.agent.id} uses removed backend langgraph; use langchain-v1 or deepagent`);
378
406
  }
379
407
  if (isLangChainBinding(binding)) {
380
- return this.createLangChainRunnable(binding);
408
+ return this.createLangChainRunnable(binding, { threadId: options.threadId });
381
409
  }
382
410
  return this.createDeepAgentRunnable(binding);
383
411
  }
@@ -425,25 +453,34 @@ export class AgentRuntimeAdapter {
425
453
  });
426
454
  return createDeepAgent(deepAgentConfig);
427
455
  }
428
- async create(binding) {
429
- const cached = this.runnableCache.get(binding);
456
+ buildRunnableCacheKey(binding, threadId) {
457
+ const filesystemConfig = getBindingFilesystemConfig(binding);
458
+ const sessionStorage = typeof filesystemConfig?.sessionStorage === "object" && filesystemConfig.sessionStorage
459
+ ? filesystemConfig.sessionStorage
460
+ : undefined;
461
+ const sessionScoped = sessionStorage?.enabled === true;
462
+ return `${binding.agent.sourcePath}::${sessionScoped ? (threadId ?? "__default__") : "__binding__"}`;
463
+ }
464
+ async create(binding, options = {}) {
465
+ const cacheKey = this.buildRunnableCacheKey(binding, options.threadId);
466
+ const cached = this.runnableCache.get(cacheKey);
430
467
  if (cached) {
431
468
  return cached;
432
469
  }
433
- const pending = this.createRunnable(binding);
434
- this.runnableCache.set(binding, pending);
470
+ const pending = this.createRunnable(binding, options);
471
+ this.runnableCache.set(cacheKey, pending);
435
472
  try {
436
473
  return await pending;
437
474
  }
438
475
  catch (error) {
439
- this.runnableCache.delete(binding);
476
+ this.runnableCache.delete(cacheKey);
440
477
  throw error;
441
478
  }
442
479
  }
443
480
  async invoke(binding, input, threadId, runId, resumePayload, history = [], options = {}) {
444
481
  const callRuntime = async (activeBinding, activeRequest) => {
445
482
  return this.invokeWithProviderRetry(activeBinding, async () => {
446
- const runnable = await this.create(activeBinding);
483
+ const runnable = await this.create(activeBinding, { threadId });
447
484
  return (await this.withTimeout(() => runnable.invoke(activeRequest, resolveLangChainInvocationConfig(activeBinding, {
448
485
  threadId,
449
486
  runId,
@@ -469,7 +506,7 @@ export class AgentRuntimeAdapter {
469
506
  invokeOptions: options,
470
507
  resolveTools: (tools, currentBinding) => this.resolveTools(tools, currentBinding),
471
508
  getToolNameMapping: (currentBinding) => this.getToolNameMapping(currentBinding),
472
- resolveBuiltinMiddlewareTools: (currentBinding, currentOptions) => this.resolveBuiltinMiddlewareTools(currentBinding, currentOptions),
509
+ resolveBuiltinMiddlewareTools: (currentBinding, currentOptions) => this.resolveBuiltinMiddlewareTools(currentBinding, { ...currentOptions, threadId }),
473
510
  callRuntimeWithToolParseRecovery,
474
511
  });
475
512
  }
@@ -494,7 +531,7 @@ export class AgentRuntimeAdapter {
494
531
  forceInvokeFallback,
495
532
  canUseDirectModelStream,
496
533
  langChainStreamModel,
497
- createRunnable: () => this.create(binding),
534
+ createRunnable: () => this.create(binding, { threadId }),
498
535
  withTimeout: (producer, timeoutMs, operation, stage) => this.withTimeout(producer, timeoutMs, operation, stage),
499
536
  iterateWithTimeout: (iterable, timeoutMs, operation, deadlineAt, deadlineTimeoutMs) => this.iterateWithTimeout(iterable, timeoutMs, operation, deadlineAt, deadlineTimeoutMs),
500
537
  invokeTimeoutMs: computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs),
@@ -0,0 +1,2 @@
1
+ import type { CompiledAgentBinding, RuntimeGovernanceBundle } from "../../../contracts/types.js";
2
+ export declare function buildRuntimeGovernanceBundles(binding: CompiledAgentBinding): RuntimeGovernanceBundle[];
@@ -0,0 +1,76 @@
1
+ import { getBindingPrimaryTools } from "../../support/compiled-binding.js";
2
+ import { toolRequiresRuntimeApproval } from "../../adapter/tool/tool-hitl.js";
3
+ const WRITE_LIKE_PATTERN = /\b(write|edit|delete|create|update|append|insert|push|commit|publish|send|post|apply|merge|sync|upload|save)\b/i;
4
+ function inputHints(binding, tool) {
5
+ const hints = new Set();
6
+ const target = `${tool.name} ${tool.description}`.toLowerCase();
7
+ if (/(path|file|dir|directory|folder|workspace|repo|repository)/.test(target)) {
8
+ hints.add("filesystem-scope");
9
+ }
10
+ if (/(memory|knowledge|store|recall)/.test(target)) {
11
+ hints.add("memory-scope");
12
+ }
13
+ if (tool.config?.mcp) {
14
+ hints.add("remote-mcp");
15
+ }
16
+ if (binding.agent.executionMode === "deepagent") {
17
+ hints.add("delegated-runtime");
18
+ }
19
+ return Array.from(hints);
20
+ }
21
+ function classifyRisk(policy) {
22
+ if (policy.requiresApproval) {
23
+ return "high";
24
+ }
25
+ const target = `${policy.toolName} ${policy.description}`;
26
+ if (policy.toolType === "mcp" && WRITE_LIKE_PATTERN.test(target)) {
27
+ return "high";
28
+ }
29
+ if (policy.toolType === "backend" || policy.toolType === "mcp") {
30
+ return "medium";
31
+ }
32
+ return "low";
33
+ }
34
+ function toCategory(toolType) {
35
+ if (toolType === "mcp") {
36
+ return "mcp";
37
+ }
38
+ if (toolType === "backend") {
39
+ return "backend";
40
+ }
41
+ if (toolType === "provider") {
42
+ return "provider-native";
43
+ }
44
+ return "local";
45
+ }
46
+ export function buildRuntimeGovernanceBundles(binding) {
47
+ const toolPolicies = getBindingPrimaryTools(binding).map((tool) => {
48
+ const requiresApproval = toolRequiresRuntimeApproval(tool);
49
+ return {
50
+ toolName: tool.name,
51
+ toolId: tool.id,
52
+ toolType: tool.type,
53
+ category: toCategory(tool.type),
54
+ risk: classifyRisk({
55
+ toolType: tool.type,
56
+ requiresApproval,
57
+ toolName: tool.name,
58
+ description: tool.description,
59
+ config: tool.config,
60
+ }),
61
+ requiresApproval,
62
+ approvalPolicy: tool.hitl?.enabled === true ? "explicit-hitl" : requiresApproval ? "runtime-default" : "none",
63
+ hasInputSchema: typeof tool.inputSchemaRef === "string" && tool.inputSchemaRef.trim().length > 0,
64
+ inputRiskHints: inputHints(binding, tool),
65
+ };
66
+ });
67
+ if (toolPolicies.length === 0) {
68
+ return [];
69
+ }
70
+ return [{
71
+ bundleId: `governance/${binding.agent.id}`,
72
+ title: "Runtime tool governance",
73
+ summary: `${toolPolicies.filter((tool) => tool.requiresApproval).length} of ${toolPolicies.length} tool(s) require approval`,
74
+ toolPolicies,
75
+ }];
76
+ }
@@ -1,5 +1,6 @@
1
1
  import { readSkillMetadata } from "../../support/skill-metadata.js";
2
2
  import { getBindingMemorySources, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSkills, getBindingSubagents, } from "../../support/compiled-binding.js";
3
+ import { buildRuntimeGovernanceBundles } from "./governance.js";
3
4
  function asObject(value) {
4
5
  return typeof value === "object" && value !== null ? value : null;
5
6
  }
@@ -67,6 +68,9 @@ export function buildRunRuntimeSnapshot(binding, options) {
67
68
  };
68
69
  }),
69
70
  memory: getBindingMemorySources(binding),
71
+ governance: {
72
+ bundles: buildRuntimeGovernanceBundles(binding),
73
+ },
70
74
  ...(tracing ? { tracing } : {}),
71
75
  };
72
76
  }
@@ -1,7 +1,8 @@
1
- import type { CompiledAgentBinding } from "../../../contracts/types.js";
1
+ import type { CompiledAgentBinding, RuntimeGovernanceBundle } from "../../../contracts/types.js";
2
2
  export type PolicyEngineDecision = {
3
3
  allowed: boolean;
4
4
  reasons: string[];
5
+ bundles?: RuntimeGovernanceBundle[];
5
6
  };
6
7
  export declare class PolicyEngine {
7
8
  /**
@@ -7,6 +7,7 @@ export class PolicyEngine {
7
7
  */
8
8
  evaluate(binding) {
9
9
  const reasons = [];
10
+ const bundles = [];
10
11
  let allowed = true;
11
12
  for (const evaluator of getPolicyEvaluators()) {
12
13
  const decision = evaluator.evaluate(binding);
@@ -17,8 +18,11 @@ export class PolicyEngine {
17
18
  allowed = false;
18
19
  }
19
20
  reasons.push(...decision.reasons);
21
+ if (Array.isArray(decision.bundles)) {
22
+ bundles.push(...decision.bundles);
23
+ }
20
24
  }
21
- return { allowed, reasons };
25
+ return bundles.length > 0 ? { allowed, reasons, bundles } : { allowed, reasons };
22
26
  }
23
27
  }
24
28
  export { PolicyEngine as GovernanceEngine, };
@@ -15,6 +15,9 @@ export declare function renderMemoryRecordsMarkdown(title: string, records: Memo
15
15
  export declare function rebuildStructuredMemoryProjections(store: StoreLike, namespace: string[], title: string, scope: MemoryScope, maxEntries: number): Promise<void>;
16
16
  export declare function listMemoryRecordsForScopes(store: StoreLike, scopes: MemoryScope[]): Promise<MemoryRecord[]>;
17
17
  export declare function getMemoryRecord(store: StoreLike, scope: MemoryScope, recordId: string): Promise<MemoryRecord | null>;
18
+ export declare function findMemoryRecordById(store: StoreLike, recordId: string): Promise<MemoryRecord | null>;
19
+ export declare function updateMemoryRecord(store: StoreLike, record: MemoryRecord, updatedAt: string): Promise<MemoryRecord>;
20
+ export declare function removeMemoryRecord(store: StoreLike, scope: MemoryScope, recordId: string): Promise<MemoryRecord | null>;
18
21
  export declare function persistStructuredMemoryRecords(options: PersistMemoryRecordsOptions): Promise<{
19
22
  records: MemoryRecord[];
20
23
  decisions: MemoryDecision[];
@@ -34,6 +34,12 @@ function createCanonicalKey(candidate, kind, scope) {
34
34
  function buildScopeNamespace(scope) {
35
35
  return ["memories", "records", scope];
36
36
  }
37
+ function buildCanonicalIndexNamespace(scope) {
38
+ return ["memories", "indexes", "canonical", scope];
39
+ }
40
+ function buildSourceRefIndexNamespace(scope) {
41
+ return ["memories", "indexes", "source-ref", scope];
42
+ }
37
43
  function normalizeTextForCompare(value) {
38
44
  return value.toLowerCase().replace(/\s+/g, " ").trim();
39
45
  }
@@ -312,7 +318,7 @@ function evaluateDecision(existing, incoming, recordedAt) {
312
318
  async function putRecordWithIndexes(store, record, updatedAt) {
313
319
  const canonicalKeyId = createFingerprint(record.canonicalKey);
314
320
  await store.put(buildScopeNamespace(record.scope), `${record.id}.json`, record);
315
- await store.put(["memories", "indexes", "canonical", record.scope], `${canonicalKeyId}-${record.id}.json`, {
321
+ await store.put(buildCanonicalIndexNamespace(record.scope), `${canonicalKeyId}-${record.id}.json`, {
316
322
  canonicalKey: record.canonicalKey,
317
323
  recordId: record.id,
318
324
  scope: record.scope,
@@ -321,7 +327,7 @@ async function putRecordWithIndexes(store, record, updatedAt) {
321
327
  });
322
328
  await Promise.all(record.sourceRefs.map((sourceRef) => {
323
329
  const sourceRefId = createFingerprint(sourceRef);
324
- return store.put(["memories", "indexes", "source-ref", record.scope], `${sourceRefId}-${record.id}.json`, {
330
+ return store.put(buildSourceRefIndexNamespace(record.scope), `${sourceRefId}-${record.id}.json`, {
325
331
  sourceRef,
326
332
  recordId: record.id,
327
333
  scope: record.scope,
@@ -384,6 +390,46 @@ export async function getMemoryRecord(store, scope, recordId) {
384
390
  const candidate = record;
385
391
  return typeof candidate.id === "string" && typeof candidate.content === "string" ? candidate : null;
386
392
  }
393
+ export async function findMemoryRecordById(store, recordId) {
394
+ for (const scope of MEMORY_SCOPES) {
395
+ const record = await getMemoryRecord(store, scope, recordId);
396
+ if (record) {
397
+ return record;
398
+ }
399
+ }
400
+ return null;
401
+ }
402
+ async function deleteRecordIndexes(store, record) {
403
+ const [canonicalEntries, sourceRefEntries] = await Promise.all([
404
+ store.search(buildCanonicalIndexNamespace(record.scope)),
405
+ store.search(buildSourceRefIndexNamespace(record.scope)),
406
+ ]);
407
+ await Promise.all([
408
+ ...canonicalEntries
409
+ .filter((entry) => entry.value?.recordId === record.id)
410
+ .map((entry) => store.delete(entry.namespace, entry.key)),
411
+ ...sourceRefEntries
412
+ .filter((entry) => entry.value?.recordId === record.id)
413
+ .map((entry) => store.delete(entry.namespace, entry.key)),
414
+ ]);
415
+ }
416
+ export async function updateMemoryRecord(store, record, updatedAt) {
417
+ const existing = await getMemoryRecord(store, record.scope, record.id);
418
+ if (existing) {
419
+ await deleteRecordIndexes(store, existing);
420
+ }
421
+ await putRecordWithIndexes(store, record, updatedAt);
422
+ return record;
423
+ }
424
+ export async function removeMemoryRecord(store, scope, recordId) {
425
+ const existing = await getMemoryRecord(store, scope, recordId);
426
+ if (!existing) {
427
+ return null;
428
+ }
429
+ await deleteRecordIndexes(store, existing);
430
+ await store.delete(buildScopeNamespace(scope), `${recordId}.json`);
431
+ return existing;
432
+ }
387
433
  export async function persistStructuredMemoryRecords(options) {
388
434
  const existingRecords = await listStoredRecords(options.store);
389
435
  const persistedRecords = [];