@jini-ai/daemon 0.6.15-rc.2 → 0.6.15

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 (49) hide show
  1. package/dist/agent-executor/codex/home-config.js +3 -2
  2. package/dist/agent-executor/codex/home-staging.js +2 -2
  3. package/dist/agent-executor/codex/isolation.js +6 -4
  4. package/dist/agent-executor/event-translation.js +2 -2
  5. package/dist/agent-executor/events.js +64 -48
  6. package/dist/agent-executor/launch.js +30 -46
  7. package/dist/agent-executor/live-session-state.js +2 -2
  8. package/dist/agent-executor/mcp-bridge.js +5 -0
  9. package/dist/agent-executor/message-relay.js +5 -6
  10. package/dist/agent-executor/run-output.js +7 -2
  11. package/dist/close-status.js +6 -2
  12. package/dist/core/omit-keys.d.ts +7 -0
  13. package/dist/core/omit-keys.js +12 -0
  14. package/dist/core/read-only-tools.js +5 -1
  15. package/dist/core/tool-gaps.js +1 -1
  16. package/dist/delegated-result-correlation.js +2 -2
  17. package/dist/http/agents.d.ts +4 -0
  18. package/dist/http/attachments.js +13 -12
  19. package/dist/http/component-catalog.js +1 -1
  20. package/dist/http/connectors.js +7 -5
  21. package/dist/http/db-ops.js +5 -0
  22. package/dist/http/delegated-tools.js +5 -0
  23. package/dist/http/frontend-sessions.js +7 -5
  24. package/dist/http/host-tools.js +7 -5
  25. package/dist/http/memory.js +3 -1
  26. package/dist/http/model-proxy.js +13 -8
  27. package/dist/http/research-tavily.adapter.js +2 -1
  28. package/dist/http/routines.js +7 -5
  29. package/dist/http/terminals.js +5 -0
  30. package/dist/http/tool-catalog.js +1 -1
  31. package/dist/http/xai.js +10 -7
  32. package/dist/legacy-data-migration.d.ts +0 -12
  33. package/dist/legacy-data-migration.js +21 -13
  34. package/dist/remote-tool-executor.js +2 -1
  35. package/dist/routines/schedule.js +2 -1
  36. package/dist/routines/scheduler.js +6 -7
  37. package/dist/run-lifecycle.js +2 -1
  38. package/dist/store/agent-sessions/sql.js +7 -3
  39. package/dist/store/event-log/sqlite-kernel.js +5 -1
  40. package/dist/store/event-log/sqlite.js +6 -2
  41. package/dist/surface-exchanges.js +3 -3
  42. package/dist/terminal-session.js +4 -5
  43. package/dist/tool-executor.js +47 -3
  44. package/dist/tool-recovery/recovery.js +5 -3
  45. package/dist/tool-recovery/redaction.js +1 -2
  46. package/dist/tool-recovery/stack.js +5 -1
  47. package/dist/tool-result-media.js +7 -1
  48. package/dist/tool-result-surfaces.js +10 -9
  49. package/package.json +20 -15
@@ -13,6 +13,12 @@ export function validateSessionKernel({ kernel, dialect, transports }) {
13
13
  function runSessionQuery(kernel, body) {
14
14
  return kernel.run(db => body(db));
15
15
  }
16
+ /** The one atomic upsert behind `setSessionId`: insert the pair, or replace its session id and time. */
17
+ function upsertSessionQuery({ conversationId, agentId, sessionId, updatedAt }) {
18
+ return (db) => db.insertInto('assistant_agent_sessions')
19
+ .values({ conversation_id: conversationId, agent_id: agentId, session_id: sessionId, updated_at: updatedAt })
20
+ .onConflict(oc => oc.columns(['conversation_id', 'agent_id']).doUpdateSet({ session_id: sessionId, updated_at: updatedAt })).execute();
21
+ }
16
22
  /** Translate opaque driver failures without losing their cause or mutating host state. */
17
23
  async function atSessionBoundary(operation) {
18
24
  try {
@@ -43,9 +49,7 @@ export function createSqlAgentSessionStore({ kernel }, { clock = createSystemClo
43
49
  setSessionId({ conversationId, agentId, sessionId }) {
44
50
  return atSessionBoundary(async () => {
45
51
  const updatedAt = clock.nowMs();
46
- await runSessionQuery(kernel, db => db.insertInto('assistant_agent_sessions')
47
- .values({ conversation_id: conversationId, agent_id: agentId, session_id: sessionId, updated_at: updatedAt })
48
- .onConflict(oc => oc.columns(['conversation_id', 'agent_id']).doUpdateSet({ session_id: sessionId, updated_at: updatedAt })).execute());
52
+ await runSessionQuery(kernel, upsertSessionQuery({ conversationId, agentId, sessionId, updatedAt }));
49
53
  });
50
54
  },
51
55
  clearSessionId({ conversationId, agentId }) {
@@ -129,7 +129,11 @@ async function replayInKernel({ db, runId, afterCursor }) {
129
129
  const result = replayResult({ run: { run_id: runId, next_cursor: first.next_cursor }, oldest, afterCursor });
130
130
  if (result.kind !== 'ok')
131
131
  return result;
132
- const entries = rows.flatMap(({ cursor, event, data, recorded_at }) => cursor === null || event === null || data === null || recorded_at === null ? [] : [rowToEntry({ cursor, event, data, recorded_at })]);
132
+ // A row missing any event column carries no entry.
133
+ const entries = rows.flatMap(({ cursor, event, data, recorded_at }) => {
134
+ const missing = cursor === null || event === null;
135
+ return missing || data === null || recorded_at === null ? [] : [rowToEntry({ cursor, event, data, recorded_at })];
136
+ });
133
137
  return { ...result, entries };
134
138
  }
135
139
  //# sourceMappingURL=sqlite-kernel.js.map
@@ -58,8 +58,12 @@ export function openSqliteEventLog(input, options = {}) {
58
58
  } }, { pragmas: ['journal_mode = WAL'] });
59
59
  const log = bindSqliteEventLog(db, options);
60
60
  let closed = false;
61
- return { ...log, async close(_args) { if (closed)
62
- return; closed = true; db.close(); } };
61
+ return { ...log, async close(_args) {
62
+ if (closed)
63
+ return;
64
+ closed = true;
65
+ db.close();
66
+ } };
63
67
  }
64
68
  catch (error) {
65
69
  try {
@@ -143,9 +143,9 @@ export function createSurfaceExchangeStore(deps, { idleTtlMs = DEFAULT_SURFACE_I
143
143
  // Unknown and closed are the same situation to a human whose dialog is no longer waiting.
144
144
  if (!entry)
145
145
  return { ok: false, reason: 'unknown-or-closed' };
146
- if ((spec.toolId !== undefined && entry.binding.toolId !== spec.toolId)
147
- || (entry.binding.channel ?? deps.defaultChannel) !== (spec.channel ?? deps.defaultChannel)
148
- || entry.binding.principalId !== spec.principalId) {
146
+ const toolMismatch = spec.toolId !== undefined && entry.binding.toolId !== spec.toolId;
147
+ const channelMismatch = (entry.binding.channel ?? deps.defaultChannel) !== (spec.channel ?? deps.defaultChannel);
148
+ if (toolMismatch || channelMismatch || entry.binding.principalId !== spec.principalId) {
149
149
  // A wrong-binding delivery must not consume a conversation the right human is still having.
150
150
  return { ok: false, reason: 'binding-mismatch' };
151
151
  }
@@ -200,11 +200,10 @@ export function createTerminalSessionManager(_requiredArgs, options = {}) {
200
200
  const result = [];
201
201
  for (const session of terminalService.list()) {
202
202
  const meta = metadata.get(session.id);
203
- if (!meta || meta.principalId !== principal.id)
204
- continue;
205
- if (filter.resourceRef !== undefined && meta.resourceRef !== filter.resourceRef)
206
- continue;
207
- result.push(toSessionInfo(session, meta.resourceRef));
203
+ const visible = meta !== undefined && meta.principalId === principal.id
204
+ && (filter.resourceRef === undefined || meta.resourceRef === filter.resourceRef);
205
+ if (visible)
206
+ result.push(toSessionInfo(session, meta.resourceRef));
208
207
  }
209
208
  return result;
210
209
  }
@@ -46,7 +46,7 @@
46
46
  * answer gets back in.
47
47
  */
48
48
  import { randomUUID } from 'node:crypto';
49
- import { ToolInputError, withNormalizationNotes, ToolInputNormalizationError, } from '@jini-ai/core';
49
+ import { ToolInputError, withNormalizationNotes, ToolInputNormalizationError, isReadOnlyTool, } from '@jini-ai/core';
50
50
  import { authorizeToolInvocation } from '@jini-ai/core/composition';
51
51
  import { toolCandidates } from './core/tool-candidates.js';
52
52
  /**
@@ -104,6 +104,8 @@ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
104
104
  // Finished executionIds, oldest first (Set insertion order): the only records `retireAudit` evicts.
105
105
  const finishedAudits = new Set();
106
106
  const pendingConfirmations = new Map();
107
+ // Owned by the executor, not a bridge: concurrent HTTP/MCP calls create separate bridges.
108
+ const approvalWaits = new Map();
107
109
  const activeControllers = new Map();
108
110
  /**
109
111
  * Marks a pending confirmation `cancel()` just resolved (as opposed to a genuine human `deny`),
@@ -114,6 +116,34 @@ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
114
116
  * `requestConfirmation()` settles, so it can never leak onto a later execution.
115
117
  */
116
118
  const cancelledConfirmations = new Set();
119
+ /** Records only actual human waits; terminal decisions and aborts release the run's write block.
120
+ * @complexity O(1) time/space per pending execution; empty run entries are removed. */
121
+ function approvalStateChange({ run, executionId, toolId, pending }, _optional = {}) {
122
+ if (pending) {
123
+ const waits = approvalWaits.get(run.id) ?? new Map();
124
+ waits.set(executionId, toolId);
125
+ approvalWaits.set(run.id, waits);
126
+ return;
127
+ }
128
+ const waits = approvalWaits.get(run.id);
129
+ waits?.delete(executionId);
130
+ if (waits?.size === 0)
131
+ approvalWaits.delete(run.id);
132
+ }
133
+ /** Refuses writes using the registered read-only contract, never a model's claim.
134
+ * The validation result reaches every delegated transport as an actionable error, without
135
+ * resolving or approving the original call. @complexity O(1) time/space. */
136
+ function approvalRefusal({ executionId, run, descriptor }, _optional = {}) {
137
+ const pendingToolId = approvalWaits.get(run.id)?.values().next().value;
138
+ if (pendingToolId === undefined || isReadOnlyTool({ descriptor }))
139
+ return undefined;
140
+ const error = JSON.stringify({
141
+ status: 'awaiting_approval', started: false, toolId: descriptor.id, pendingToolId,
142
+ message: 'Another action is waiting for human confirmation and has not started. This write was refused. Reads may continue. Never say an action has started until its tool result explicitly says it started.',
143
+ });
144
+ appendEvent(executionId, 'failed', error);
145
+ return { executionId, status: 'failed', errorKind: 'validation', error };
146
+ }
117
147
  function appendEvent(executionId, phase, detail) {
118
148
  // Every call site passes an `executionId` this same `execute()` call
119
149
  // already registered in `audits` (right before its first `appendEvent`
@@ -242,7 +272,7 @@ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
242
272
  * Acts on the authorization outcome synchronously: `{ refusal }` if the call ends here
243
273
  * (cancelled meanwhile, or denied), otherwise records `authorized` and returns `{ allowed }`.
244
274
  */
245
- function admitAuthorized({ executionId, resolved, signal }) {
275
+ function admitAuthorized({ executionId, resolved, signal, run }) {
246
276
  // `authorize()` isn't itself signal-aware — it's typically a fast policy check, not a
247
277
  // human-in-the-loop wait — so this is a post-await check rather than a mid-flight abort, the
248
278
  // same idiom the post-handler check below already uses: a signal that fired while `authorize()`
@@ -253,6 +283,10 @@ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
253
283
  return { refusal: terminal({ executionId, status: 'denied' }) };
254
284
  const { normalizationNotes } = resolved;
255
285
  appendEvent(executionId, 'authorized', normalizationNotes.length ? `normalized: ${normalizationNotes.join('; ')}` : undefined);
286
+ // Check after authorization awaits: an approval may have opened while this policy was pending.
287
+ const refusal = approvalRefusal({ executionId, run, descriptor: resolved.descriptor });
288
+ if (refusal)
289
+ return { refusal };
256
290
  return { allowed: resolved };
257
291
  }
258
292
  /**
@@ -268,13 +302,18 @@ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
268
302
  async function confirmGate(request) {
269
303
  const { executionId } = request;
270
304
  let confirmation;
305
+ const state = { run: request.run, executionId, toolId: request.tool.id };
271
306
  try {
307
+ approvalStateChange({ ...state, pending: true });
272
308
  confirmation = await requestConfirmation(request);
273
309
  }
274
310
  catch {
275
311
  // Same reasoning as the authorization gate: terminal, recorded, and generic.
276
312
  return operationalFailure({ executionId, error: 'confirmation failed' });
277
313
  }
314
+ finally {
315
+ approvalStateChange({ ...state, pending: false });
316
+ }
278
317
  if (confirmation === 'confirm') {
279
318
  appendEvent(executionId, 'confirmed');
280
319
  return undefined;
@@ -353,6 +392,10 @@ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
353
392
  if (controller.signal.aborted) {
354
393
  return terminal({ executionId, status: 'cancelled' });
355
394
  }
395
+ // Guidance may await I/O after admission; another call can open a confirmation in that gap.
396
+ const refusal = approvalRefusal({ executionId, run, descriptor });
397
+ if (refusal)
398
+ return refusal;
356
399
  const timeout = startTimeout(controller, descriptor.timeoutMs);
357
400
  // Cleanup (clear the timeout) is repeated at
358
401
  // each return below rather than centralized in a `finally` — a
@@ -379,6 +422,7 @@ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
379
422
  signal: controller.signal,
380
423
  }, {
381
424
  ...surfaceOption(emitSurface),
425
+ onApprovalStateChange: ({ pending }) => approvalStateChange({ run, executionId, toolId: call.toolId, pending }),
382
426
  invokeTool: nestedInvoker({ principal, run, toolId: call.toolId, invocationRegistry: call.invocationRegistry, signal: controller.signal }, { emitSurface }),
383
427
  });
384
428
  cleanup();
@@ -443,7 +487,7 @@ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
443
487
  // `invocationRegistry.has({ toolId })` above already confirmed the tool exists, and `ToolRegistry` is
444
488
  // append-only (no unregister — see `createToolRegistry`'s doc), so `authorizeToolInvocation`
445
489
  // resolving the same `toolId` against the same `registry` cannot come back `undefined` here.
446
- const gate = admitAuthorized({ executionId, resolved: resolved, signal: controller.signal });
490
+ const gate = admitAuthorized({ executionId, resolved: resolved, signal: controller.signal, run });
447
491
  if ('refusal' in gate) {
448
492
  cleanupCancellation();
449
493
  return gate.refusal;
@@ -1,5 +1,5 @@
1
1
  import { isIssuedToolFailureDiagnostic } from "./diagnostics.js";
2
- import { askOnce, SURFACE_DISMISSED_PARAM, SURFACE_EXCHANGE_ID_PARAM, } from "../surface-exchanges.js";
2
+ import { askOnce, SURFACE_DISMISSED_PARAM, } from "../surface-exchanges.js";
3
3
  import { refuseNonReadOnlyDispatch } from "../core/read-only-tools.js";
4
4
  /**
5
5
  * @file The CONSUMER of `contracts/core/tool-failure-diagnostics.ts`'s `ToolFailureDiagnostic`
@@ -119,7 +119,9 @@ function isActionableDiagnostic(value) {
119
119
  * @complexity O(1).
120
120
  */
121
121
  function remedyReportedFailure(output) {
122
- return isPlainObject(output) && (output["saved"] === false || output["created"] === false || output["cancelled"] === true || output["connected"] === false);
122
+ if (!isPlainObject(output))
123
+ return false;
124
+ return output["saved"] === false || output["created"] === false || output["cancelled"] === true || output["connected"] === false;
123
125
  }
124
126
  /** `true` once the bounded walk should stop descending — depth past {@link MAX_DIAGNOSTIC_SCAN_DEPTH}
125
127
  * or the node budget exhausted. Pulled out of {@link walkForDiagnostic} so that function's own guard
@@ -400,7 +402,7 @@ function remedyApplied(remedyResult) {
400
402
  export function withToolFailureRecovery({ inner, ...deps }, _optional = {}) {
401
403
  return {
402
404
  execute: async (required, optional = {}) => {
403
- const { principal, toolId, input } = required;
405
+ const { principal, input } = required;
404
406
  const { signal, emitSurface } = optional;
405
407
  const result = await inner.execute(required, optional);
406
408
  // SILENCE STAYS FREE: everything but a completed call whose output carries an actionable
@@ -148,8 +148,7 @@ function redactFailedResult(result, context, deps) {
148
148
  return result;
149
149
  return { ...result, error: deps.redactSecretShapes({ text: result.error }).text };
150
150
  }
151
- const mintErrorId = deps.mintErrorId;
152
- const onFailure = deps.onFailure;
151
+ const { mintErrorId, onFailure } = deps;
153
152
  const { text, redactions } = deps.redactSecretShapes({ text: result.error ?? `tool "${context.toolId}" failed` });
154
153
  const errorId = mintErrorId();
155
154
  safeOnFailure(onFailure, {
@@ -46,6 +46,10 @@ import { withReadOnlyToolConstraint } from "../core/read-only-tools.js";
46
46
  import { withToolAttemptAudit } from "../core/tool-audit.js";
47
47
  import { withToolFailureRecovery } from "./recovery.js";
48
48
  import { withRedactedToolFailures, readToolErrorId } from "./redaction.js";
49
+ /** The audit wrapper's options: only an error sink the caller actually supplied. */
50
+ function auditOptions(audit) {
51
+ return audit.onSinkError ? { onSinkError: audit.onSinkError } : {};
52
+ }
49
53
  /** One registry and innermost read-only gate protect original, remedy and retry alike.
50
54
  * Redaction precedes observation; recovery is outermost so each actual attempt is audited.
51
55
  * @complexity O(1) composition; each decorator owns its runtime cost.
@@ -55,7 +59,7 @@ export function createAssistantToolExecutor(deps, _optional = {}) {
55
59
  registry: deps.registry, idGenerator: { newId: deps.newExecutionId }, messages: deps.readOnlyMessages }, {});
56
60
  const redacted = withRedactedToolFailures({ inner: guarded, ...deps.toolFailures }, {});
57
61
  const audited = deps.toolAttemptAudit
58
- ? withToolAttemptAudit({ inner: redacted, ...deps.toolAttemptAudit, readErrorId: readToolErrorId }, deps.toolAttemptAudit.onSinkError ? { onSinkError: deps.toolAttemptAudit.onSinkError } : {})
62
+ ? withToolAttemptAudit({ inner: redacted, ...deps.toolAttemptAudit, readErrorId: readToolErrorId }, auditOptions(deps.toolAttemptAudit))
59
63
  : redacted;
60
64
  return withToolFailureRecovery({ ...deps, inner: audited }, {});
61
65
  }
@@ -74,6 +74,11 @@ export function resultContent({ result }) {
74
74
  return 'Tool execution cancelled.';
75
75
  case 'failed':
76
76
  return result.error ?? 'Tool execution failed.';
77
+ default: {
78
+ // Unreachable: every tool result status has a case above, and a new one fails typecheck here.
79
+ const unhandled = result.status;
80
+ throw new TypeError(`Unhandled tool result status: ${String(unhandled)}`);
81
+ }
77
82
  }
78
83
  }
79
84
  /** Projects a model-safe result into the saved event: pixels occur only in `media`.
@@ -81,9 +86,10 @@ export function resultContent({ result }) {
81
86
  * @complexity O(n) output bytes, with O(n) transient serialization space. */
82
87
  export function projectToolResultEvent({ result }, _options = {}) {
83
88
  const { remainder, media } = extractResultMedia({ output: result.output });
89
+ const outputFlagsError = isRecord(result.output) && result.output.isError === true;
84
90
  const isError = result.status !== 'completed'
85
91
  || (batchOutcomeFor({ output: result.output }) ?? 'completed') !== 'completed'
86
- || (isRecord(result.output) && result.output.isError === true);
92
+ || outputFlagsError;
87
93
  return {
88
94
  content: resultContent({ result: { ...result, output: remainder } }),
89
95
  ...(isError ? { isError: true } : {}),
@@ -52,6 +52,13 @@
52
52
  * break the feature. Anything else absent from this list is still treated as human-only.
53
53
  */
54
54
  export const MODEL_VISIBLE_BLOCK_TYPES = ['text', 'image'];
55
+ /** A record block whose string `type` is one the model may see. */
56
+ function isModelVisibleBlock(block) {
57
+ if (!isRecord(block))
58
+ return false;
59
+ const type = block['type'];
60
+ return typeof type === 'string' && MODEL_VISIBLE_BLOCK_TYPES.includes(type);
61
+ }
55
62
  function isRecord(value) {
56
63
  return typeof value === 'object' && value !== null && !Array.isArray(value);
57
64
  }
@@ -74,16 +81,10 @@ export function splitToolResultSurfaces({ output }) {
74
81
  for (const block of content) {
75
82
  // A non-record block carries no `type` to check, so it cannot be shown to be model-safe.
76
83
  // Withheld rather than passed through, per this module's fail-closed rule.
77
- if (!isRecord(block)) {
78
- surfaces.push({ type: 'unknown', value: block });
79
- continue;
80
- }
81
- const type = block['type'];
82
- if (typeof type === 'string' && MODEL_VISIBLE_BLOCK_TYPES.includes(type)) {
84
+ if (isModelVisibleBlock(block))
83
85
  modelBlocks.push(block);
84
- continue;
85
- }
86
- surfaces.push(block);
86
+ else
87
+ surfaces.push(isRecord(block) ? block : { type: 'unknown', value: block });
87
88
  }
88
89
  if (surfaces.length === 0)
89
90
  return { modelOutput: output, surfaces: [] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jini-ai/daemon",
3
- "version": "0.6.15-rc.2",
3
+ "version": "0.6.15",
4
4
  "description": "The stateful agent daemon runtime: run lifecycle, durable event-log port, the tool-execution boundary, agent executor, terminal sessions, and routines.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -129,6 +129,7 @@
129
129
  "registry": "https://registry.npmjs.org"
130
130
  },
131
131
  "devDependencies": {
132
+ "smol-toml": "1.9.1",
132
133
  "@vitest/coverage-v8": "^2.1.9",
133
134
  "node-pty": "^1.1.0",
134
135
  "better-sqlite3": "^13.0.0",
@@ -139,32 +140,35 @@
139
140
  "@types/pg": "8.23.1",
140
141
  "express": "^4.21.0",
141
142
  "@types/express": "^4.17.21",
142
- "@jini-ai/db": "0.6.15-rc.2",
143
- "@jini-ai/chat": "0.6.15-rc.2",
144
- "@jini-ai/http-kit": "0.6.15-rc.2",
145
- "@jini-ai/core": "^0.6.15-rc.2",
146
- "@jini-ai/agent-runtime": "^0.6.15-rc.2",
147
- "@jini-ai/platform": "^0.6.15-rc.2",
148
- "@jini-ai/protocol": "^0.6.15-rc.2"
143
+ "@jini-ai/db": "0.6.15",
144
+ "@jini-ai/http-kit": "0.6.15",
145
+ "@jini-ai/chat": "0.6.15",
146
+ "@jini-ai/core": "^0.6.15",
147
+ "@jini-ai/platform": "^0.6.15",
148
+ "@jini-ai/protocol": "^0.6.15",
149
+ "@jini-ai/agent-runtime": "^0.6.15"
149
150
  },
150
151
  "dependencies": {
151
- "smol-toml": "1.9.1",
152
- "@jini-ai/core": "^0.6.15-rc.2",
153
- "@jini-ai/protocol": "^0.6.15-rc.2"
152
+ "@jini-ai/core": "^0.6.15",
153
+ "@jini-ai/protocol": "^0.6.15"
154
154
  },
155
155
  "peerDependencies": {
156
+ "smol-toml": "1.9.1",
156
157
  "node-pty": "^1.1.0",
157
- "@jini-ai/db": "^0.6.15-rc.2",
158
+ "@jini-ai/db": "^0.6.10",
158
159
  "kysely": "^0.29.6",
159
160
  "better-sqlite3": "^13.0.0",
160
161
  "@electric-sql/pglite": "0.5.8",
161
162
  "pg": "^8.23.0",
162
- "@jini-ai/http-kit": "^0.6.15-rc.2",
163
+ "@jini-ai/http-kit": "^0.6.10",
163
164
  "express": "^4.21.0 || ^5.0.0",
164
- "@jini-ai/agent-runtime": "^0.6.15-rc.2",
165
- "@jini-ai/platform": "^0.6.15-rc.2"
165
+ "@jini-ai/agent-runtime": "^0.6.10",
166
+ "@jini-ai/platform": "^0.6.10"
166
167
  },
167
168
  "peerDependenciesMeta": {
169
+ "smol-toml": {
170
+ "optional": true
171
+ },
168
172
  "node-pty": {
169
173
  "optional": true
170
174
  },
@@ -200,6 +204,7 @@
200
204
  "build": "tsc -p tsconfig.json",
201
205
  "typecheck": "tsc -p tsconfig.json --noEmit",
202
206
  "test": "vitest run",
207
+ "test:live:claude-bash": "vitest run --config vitest.claude-bash.live.config.ts",
203
208
  "test:coverage": "vitest run --coverage",
204
209
  "test:postgres": "vitest run --config vitest.postgres.config.ts src/store/agent-sessions/__tests__/postgres.postgres.test.ts"
205
210
  }