@jini-ai/daemon 0.6.10 → 0.6.12

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.
@@ -1,8 +1,13 @@
1
1
  import type { ToolDescriptor } from '@jini-ai/core';
2
- /** Registered prefix matches for unknown-tool diagnostics, in catalog order, capped at eight.
3
- * @complexity O(n) time and space over n registered tools. */
4
- export declare function toolCandidates({ toolId, tools }: {
2
+ /** Registered prefix/near-name suggestions, closest first and capped at eight. Equal scores keep
3
+ * catalog order; an exact content_read.<resource> takes priority even beyond the old cutoff.
4
+ * Suggestions grant no execution authority; the caller must retry through the existing gates.
5
+ * @example toolCandidates({ toolId: 'content_read', input: { resource: 'theme' }, tools });
6
+ * @complexity O(n*l*r + n log n) time, O(n + r) space; scoring lengths capped at 128.
7
+ */
8
+ export declare function toolCandidates({ toolId, tools, input }: {
5
9
  toolId: string;
6
10
  tools: readonly Pick<ToolDescriptor, 'id'>[];
11
+ input?: unknown;
7
12
  }, _optional?: Record<string, never>): string[];
8
13
  //# sourceMappingURL=tool-candidates.d.ts.map
@@ -1,6 +1,41 @@
1
- /** Registered prefix matches for unknown-tool diagnostics, in catalog order, capped at eight.
2
- * @complexity O(n) time and space over n registered tools. */
3
- export function toolCandidates({ toolId, tools }, _optional = {}) {
4
- return tools.map((tool) => tool.id).filter((id) => id.startsWith(toolId)).slice(0, 8);
1
+ /** Bounded Levenshtein distance for diagnostic ranking; oversized names retain prefix discovery.
2
+ * @returns Edit count, or Infinity above the 128-character scoring limit (no effects).
3
+ * @complexity O(l*r) time and O(r) space, with both name lengths capped at 128.
4
+ */
5
+ function nameDistance({ left, right }, _options = {}) {
6
+ if (left.length > 128 || right.length > 128)
7
+ return Infinity;
8
+ let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
9
+ for (let row = 1; row <= left.length; row++) {
10
+ const current = [row];
11
+ for (let column = 1; column <= right.length; column++) {
12
+ current[column] = Math.min(current[column - 1] + 1, previous[column] + 1, previous[column - 1] + Number(left[row - 1] !== right[column - 1]));
13
+ }
14
+ previous = current;
15
+ }
16
+ return previous[right.length];
17
+ }
18
+ /** Use content_read's explicit resource selector only as a diagnostic hint, never a dispatch.
19
+ * @returns The target spelling to rank against; malformed selectors use the original tool id.
20
+ * @complexity O(r) to join an r-character selector; no mutation or effects. Name scoring is bounded separately.
21
+ */
22
+ function candidateTarget({ toolId, input }, _options = {}) {
23
+ if (toolId !== 'content_read' || typeof input !== 'object' || input === null || Array.isArray(input))
24
+ return toolId;
25
+ const resource = input.resource;
26
+ return typeof resource === 'string' && resource.length > 0 ? `${toolId}.${resource}` : toolId;
27
+ }
28
+ /** Registered prefix/near-name suggestions, closest first and capped at eight. Equal scores keep
29
+ * catalog order; an exact content_read.<resource> takes priority even beyond the old cutoff.
30
+ * Suggestions grant no execution authority; the caller must retry through the existing gates.
31
+ * @example toolCandidates({ toolId: 'content_read', input: { resource: 'theme' }, tools });
32
+ * @complexity O(n*l*r + n log n) time, O(n + r) space; scoring lengths capped at 128.
33
+ */
34
+ export function toolCandidates({ toolId, tools, input }, _optional = {}) {
35
+ const target = candidateTarget({ toolId, input });
36
+ return tools.map(({ id }) => ({ id, distance: nameDistance({ left: target, right: id }) }))
37
+ .filter(({ id, distance }) => id === target || id.startsWith(toolId) || distance <= 2)
38
+ .sort((left, right) => Number(right.id === target) - Number(left.id === target) || left.distance - right.distance)
39
+ .slice(0, 8).map(({ id }) => id);
5
40
  }
6
41
  //# sourceMappingURL=tool-candidates.js.map
@@ -33,6 +33,19 @@ export interface AttachmentClaim {
33
33
  /** Absent only for an empty claim — there is no directory to grant when nothing was claimed. */
34
34
  batchDirectory?: string;
35
35
  }
36
+ /** Copy an already authenticated claim into the host's filesystem boundary. Keep the original
37
+ * store records intact for promotion/read-back; the caller owns cleanup after the run ends.
38
+ * Only claimed files are copied, never batch sidecars or another upload's capability ids.
39
+ * O(b) I/O for b claimed bytes, bounded by the attachment store's batch limits. Partial copies
40
+ * are removed before rejection. The temporary directory is private and directly under real root.
41
+ */
42
+ export declare function stageAttachmentClaim(required: {
43
+ claim: AttachmentClaim;
44
+ filesystemRoot: string;
45
+ }, _optional?: {}): Promise<{
46
+ claim: AttachmentClaim;
47
+ cleanup: () => Promise<void>;
48
+ }>;
36
49
  /** Why an attachment operation was refused. Maps to an HTTP status in `statusForRejection`. */
37
50
  export type AttachmentRejectionReason =
38
51
  /** The batch id is missing or not of the accepted shape. */
@@ -99,13 +99,39 @@
99
99
  * correlation-id-bearing generic `INTERNAL_ERROR`, with the real error reaching a host-owned sink —
100
100
  * the same `reportInternalError` shape `media.ts`/`delegated-tools.ts` use.
101
101
  */
102
- import { chmod, lstat, mkdir, open, readFile, readdir, realpath, rm, rmdir, writeFile } from 'node:fs/promises';
102
+ import { chmod, copyFile, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rm, rmdir, writeFile } from 'node:fs/promises';
103
103
  import { randomUUID } from 'node:crypto';
104
104
  import { basename, dirname, extname, resolve } from 'node:path';
105
105
  import { createApiError } from '@jini-ai/protocol';
106
106
  import { guardSameOrigin } from '@jini-ai/http-kit';
107
107
  import { sendApiError, sendJson } from '@jini-ai/http-kit';
108
108
  import { writeJsonFileAtomicAsync } from '@jini-ai/platform/fs';
109
+ /** Copy an already authenticated claim into the host's filesystem boundary. Keep the original
110
+ * store records intact for promotion/read-back; the caller owns cleanup after the run ends.
111
+ * Only claimed files are copied, never batch sidecars or another upload's capability ids.
112
+ * O(b) I/O for b claimed bytes, bounded by the attachment store's batch limits. Partial copies
113
+ * are removed before rejection. The temporary directory is private and directly under real root.
114
+ */
115
+ export async function stageAttachmentClaim(required, _optional = {}) {
116
+ const root = await realpath(required.filesystemRoot);
117
+ const directory = await mkdtemp(resolve(root, '.agent-attachments-'));
118
+ const cleanup = () => rm(directory, { recursive: true, force: true });
119
+ try {
120
+ await chmod(directory, 0o700);
121
+ const attachments = [];
122
+ for (const attachment of required.claim.attachments) {
123
+ const target = resolve(directory, basename(attachment.path));
124
+ await copyFile(attachment.path, target);
125
+ await chmod(target, 0o600);
126
+ attachments.push({ ...attachment, path: target });
127
+ }
128
+ return { claim: { attachments, batchDirectory: directory }, cleanup };
129
+ }
130
+ catch (error) {
131
+ await cleanup();
132
+ throw error;
133
+ }
134
+ }
109
135
  /**
110
136
  * A refusal this module classified itself, as opposed to an unexpected filesystem/programming
111
137
  * error. The route pack turns the `reason` into a status code and lets the `message` through only
@@ -36,7 +36,7 @@ function checkReadOnlyConstraint(deps, input) {
36
36
  if (input.requireReadOnly !== true)
37
37
  return null;
38
38
  const refusal = readOnlyTools.checkReadOnlyTool({
39
- toolId: input.toolId, registry: deps.toolRegistry, messages: readOnlyTools.defaultDaemonMessages.readOnly,
39
+ toolId: input.toolId, input: input.input, registry: deps.toolRegistry, messages: readOnlyTools.defaultDaemonMessages.readOnly,
40
40
  });
41
41
  if (refusal === null)
42
42
  return null;
@@ -1,4 +1,5 @@
1
1
  /** Daemon route packs; generic transport primitives live in @jini-ai/http-kit. */
2
+ export { createMessageRelayClaimHandler, registerMessageRelayRoutes, type MessageRelayHttpDeps } from './message-relay.js';
2
3
  export type { ResolveWorkspaceRootOptions, WorkspaceRootRequest, WorkspaceRootResolver, } from './workspace-root.js';
3
4
  export { denyAllWorkspaceRoots, resolveWorkspaceRoot, WorkspaceRootDeniedError } from './workspace-root.js';
4
5
  export type { RunCancellationService } from './cancel-owned-runs.js';
@@ -45,7 +46,7 @@ export { createResearchSearchRoute, registerResearchRoutes, researchSearchRoute
45
46
  export type { MediaGenerateResponse, MediaHttpDeps, MediaInternalErrorContext, MediaTaskDeleteResponse, MediaTaskListResponse, MediaTaskResponse, } from './media.js';
46
47
  export { mediaGenerateRoute, mediaTaskDeleteRoute, mediaTaskGetRoute, mediaTaskListRoute, registerMediaRoutes, } from './media.js';
47
48
  export type { AttachmentClaim, AttachmentRejectionReason, AttachmentsHttpDeps, AttachmentsInternalErrorContext, AttachmentStore, AttachmentUploadResponse, CreateDiskAttachmentStoreOptions, ObservedAttachmentIdentity, PendingAttachmentSummary, RecordedAttachmentIdentity, StoredAttachment, } from './attachments.js';
48
- export { ATTACHMENTS_ROUTE_PATH, AttachmentRejectedError, createDiskAttachmentStore, detectAttachmentKind, handleAttachmentCleanup, handleAttachmentUpload, isUnchangedAttachment, registerAttachmentRoutes, sanitizeAttachmentName, writeBoundedAttachmentBody, } from './attachments.js';
49
+ export { ATTACHMENTS_ROUTE_PATH, AttachmentRejectedError, createDiskAttachmentStore, detectAttachmentKind, handleAttachmentCleanup, handleAttachmentUpload, isUnchangedAttachment, registerAttachmentRoutes, sanitizeAttachmentName, stageAttachmentClaim, writeBoundedAttachmentBody, } from './attachments.js';
49
50
  export type { XaiAuthStatusResponse, XaiHttpDeps, XaiInternalErrorContext, XaiOauthStartResponse, XaiOkResponse, XaiSearchResponse, } from './xai.js';
50
51
  export { registerXaiRoutes, xaiAuthStatusRoute, xaiOauthCancelRoute, xaiOauthCompleteRoute, xaiOauthDisconnectRoute, xaiOauthStartRoute, xaiSearchRoute, } from './xai.js';
51
52
  export * from './run-credentials.js';
@@ -1,3 +1,5 @@
1
+ /** Daemon route packs; generic transport primitives live in @jini-ai/http-kit. */
2
+ export { createMessageRelayClaimHandler, registerMessageRelayRoutes } from './message-relay.js';
1
3
  export { denyAllWorkspaceRoots, resolveWorkspaceRoot, WorkspaceRootDeniedError } from './workspace-root.js';
2
4
  export { cancelRunsOwnedBy } from './cancel-owned-runs.js';
3
5
  export { ACTIVE_CONTEXT_TTL_MS, getActiveRoute, registerActiveContextRoutes, setActiveRoute, } from './active-context.js';
@@ -21,7 +23,7 @@ export { apiHealthRoute, apiReadyRoute, apiVersionInfoRoute, healthRoute, readyR
21
23
  export { connectorsAuthSessionRoute, connectorsAuthSignInRoute, connectorsAuthSignOutRoute, connectorsAuthSignUpRoute, connectorsDbDeleteRoute, connectorsDbGetRoute, connectorsDbInsertRoute, connectorsDbQueryRoute, connectorsDbUpdateRoute, connectorsRealtimePublishRoute, connectorsStorageDeleteRoute, connectorsStorageGetRoute, connectorsStorageListRoute, connectorsStoragePutRoute, registerConnectorsRoutes, } from './connectors.js';
22
24
  export { createResearchSearchRoute, registerResearchRoutes, researchSearchRoute } from './research.js';
23
25
  export { mediaGenerateRoute, mediaTaskDeleteRoute, mediaTaskGetRoute, mediaTaskListRoute, registerMediaRoutes, } from './media.js';
24
- export { ATTACHMENTS_ROUTE_PATH, AttachmentRejectedError, createDiskAttachmentStore, detectAttachmentKind, handleAttachmentCleanup, handleAttachmentUpload, isUnchangedAttachment, registerAttachmentRoutes, sanitizeAttachmentName, writeBoundedAttachmentBody, } from './attachments.js';
26
+ export { ATTACHMENTS_ROUTE_PATH, AttachmentRejectedError, createDiskAttachmentStore, detectAttachmentKind, handleAttachmentCleanup, handleAttachmentUpload, isUnchangedAttachment, registerAttachmentRoutes, sanitizeAttachmentName, stageAttachmentClaim, writeBoundedAttachmentBody, } from './attachments.js';
25
27
  export { registerXaiRoutes, xaiAuthStatusRoute, xaiOauthCancelRoute, xaiOauthCompleteRoute, xaiOauthDisconnectRoute, xaiOauthStartRoute, xaiSearchRoute, } from './xai.js';
26
28
  export * from './run-credentials.js';
27
29
  export { createTavilyResearchProvider } from './research-tavily.adapter.js';
@@ -0,0 +1,21 @@
1
+ import type { Express, RequestHandler } from 'express';
2
+ import type { RunScopedCallerResolver } from '../run-credentials.js';
3
+ import type { AgentExecutor } from '../agent-executor/contracts.js';
4
+ export interface MessageRelayHttpDeps {
5
+ readonly agentExecutor: Pick<AgentExecutor, 'claimUserMessages'>;
6
+ /** The same live credential registry used to issue JINI_DAEMON_TOKEN at spawn.
7
+ * Explicit undefined opts into the host's existing unauthenticated loopback trust: any local
8
+ * process can claim messages. Authenticated hosts must supply their resolver, never proxy identity. */
9
+ readonly runScopedCallers: RunScopedCallerResolver | undefined;
10
+ }
11
+ /** Claim a FIFO prefix without I/O. A run credential determines scope; the URL only checks it.
12
+ * Body-supplied scope is refused. A proxy credential or another run's token cannot claim.
13
+ * @returns Express handler; errors go to the host's existing error boundary.
14
+ * @complexity O(claimed text bytes); no storage or network work outside the existing event FIFO.
15
+ */
16
+ export declare function createMessageRelayClaimHandler({ agentExecutor, runScopedCallers }: MessageRelayHttpDeps, _options?: {}): RequestHandler;
17
+ /** Mount behind the host's existing daemon gate; allow this POST for live run credentials there. */
18
+ export declare function registerMessageRelayRoutes({ app, ...deps }: {
19
+ app: Express;
20
+ } & MessageRelayHttpDeps, _options?: {}): void;
21
+ //# sourceMappingURL=message-relay.d.ts.map
@@ -0,0 +1,44 @@
1
+ import { bearerTokenFromHeader } from '@jini-ai/http-kit';
2
+ /** Claim a FIFO prefix without I/O. A run credential determines scope; the URL only checks it.
3
+ * Body-supplied scope is refused. A proxy credential or another run's token cannot claim.
4
+ * @returns Express handler; errors go to the host's existing error boundary.
5
+ * @complexity O(claimed text bytes); no storage or network work outside the existing event FIFO.
6
+ */
7
+ export function createMessageRelayClaimHandler({ agentExecutor, runScopedCallers }, _options = {}) {
8
+ return (req, res, next) => {
9
+ try {
10
+ const token = bearerTokenFromHeader({ header: req.get('authorization') });
11
+ const caller = token ? runScopedCallers?.resolveCaller({ token }) : undefined;
12
+ if (runScopedCallers && !caller) {
13
+ res.status(401).json({ error: { code: 'UNAUTHENTICATED', message: 'a live run credential is required' } });
14
+ return;
15
+ }
16
+ const runId = caller?.runId ?? req.params.runId;
17
+ if (!runId || runId !== req.params.runId) {
18
+ res.status(404).json({ error: { code: 'NOT_FOUND', message: 'run was not found' } });
19
+ return;
20
+ }
21
+ const body = req.body;
22
+ if (!validClaim(body)) {
23
+ res.status(400).json({ error: { code: 'BAD_REQUEST', message: 'body must contain only via: tool-result or check_messages' } });
24
+ return;
25
+ }
26
+ res.json({ messages: agentExecutor.claimUserMessages({ runId, via: body.via }) });
27
+ }
28
+ catch (error) {
29
+ next(error);
30
+ }
31
+ };
32
+ }
33
+ /** Strict receipt-channel validation; no caller-selected run or principal is accepted. */
34
+ function validClaim(body) {
35
+ if (body === null || typeof body !== 'object' || Array.isArray(body))
36
+ return false;
37
+ const value = body;
38
+ return Object.keys(value).length === 1 && (value.via === 'tool-result' || value.via === 'check_messages');
39
+ }
40
+ /** Mount behind the host's existing daemon gate; allow this POST for live run credentials there. */
41
+ export function registerMessageRelayRoutes({ app, ...deps }, _options = {}) {
42
+ app.post('/api/runs/:runId/messages/claim', createMessageRelayClaimHandler(deps));
43
+ }
44
+ //# sourceMappingURL=message-relay.js.map
@@ -55,12 +55,15 @@ export interface RunCancelResponse {
55
55
  readonly run: RunStatus;
56
56
  }
57
57
  export interface RunMessageRequest {
58
+ /** Generated at intake and passed unchanged to the executor's queue/consumption events. */
59
+ readonly id?: string;
58
60
  readonly runId: string;
59
61
  readonly text: string;
60
62
  /** Opaque upload capabilities; the host must authorize and prepare every ref before delivery. */
61
63
  readonly attachmentIds?: readonly string[];
62
64
  }
63
65
  export interface RunMessageResponse {
66
+ readonly id: string;
64
67
  readonly delivery: 'delivered';
65
68
  }
66
69
  /** Ceiling on one mid-run message, matching what a person can reasonably type or paste into a composer. */
@@ -81,8 +84,8 @@ export declare const runCancelRoute: import("@jini-ai/http-kit").JsonRouteSpec<{
81
84
  /**
82
85
  * `POST /api/runs/:runId/messages` — a message the human sent while the run is going. `202` when it
83
86
  * reached the live agent; `409 CONFLICT` with `details.delivery` (`'unsupported'` or
84
- * `'not-running'`) when it did not, which tells the client whether to interrupt the run and send the
85
- * text as the next turn, or simply send it next because the run is already ending.
87
+ * `'not-running'`) when it did not, which tells the client to retain the next-turn queue without canceling live work.
88
+ * Acceptance is distinct from the UUID-attributed consumption receipt in the run stream.
86
89
  */
87
90
  export declare const runMessageRoute: import("@jini-ai/http-kit").JsonRouteSpec<RunMessageRequest, RunMessageResponse, RunHttpDeps, undefined>;
88
91
  /**
package/dist/http/runs.js CHANGED
@@ -154,8 +154,8 @@ export const runCancelRoute = defineJsonRoute({
154
154
  /**
155
155
  * `POST /api/runs/:runId/messages` — a message the human sent while the run is going. `202` when it
156
156
  * reached the live agent; `409 CONFLICT` with `details.delivery` (`'unsupported'` or
157
- * `'not-running'`) when it did not, which tells the client whether to interrupt the run and send the
158
- * text as the next turn, or simply send it next because the run is already ending.
157
+ * `'not-running'`) when it did not, which tells the client to retain the next-turn queue without canceling live work.
158
+ * Acceptance is distinct from the UUID-attributed consumption receipt in the run stream.
159
159
  */
160
160
  export const runMessageRoute = defineJsonRoute({
161
161
  method: 'post', path: '/api/runs/:runId/messages', parse: parseRunMessage, handle: async ({ input, deps }) => {
@@ -163,9 +163,10 @@ export const runMessageRoute = defineJsonRoute({
163
163
  if (run === undefined)
164
164
  return err({ error: createApiError({ code: 'NOT_FOUND', message: `run "${input.runId}" was not found` }) });
165
165
  const deliver = input.attachmentIds?.length ? deps.deliverUserMessageWithAttachments : deps.deliverUserMessage;
166
- const delivery = deliver ? await deliver(input) : 'unsupported';
166
+ const id = randomUUID();
167
+ const delivery = deliver ? await deliver({ ...input, id }) : 'unsupported';
167
168
  if (delivery === 'delivered')
168
- return ok({ value: { delivery } });
169
+ return ok({ value: { delivery, id } });
169
170
  return err({ error: createApiError({ code: 'CONFLICT', message: `run "${input.runId}" did not take the message` }, { details: { delivery } }) });
170
171
  }
171
172
  }, { requireSameOrigin: true, successStatus: 202 });
@@ -208,7 +209,12 @@ export async function handleRunEventStreamRequest({ res, runId, afterCursor, dep
208
209
  }
209
210
  });
210
211
  try {
211
- const subscribed = await deps.lifecycle.stream({ runId, onEvent: (event) => channel.enqueue({ event }) }, { afterCursor });
212
+ const subscribed = await deps.lifecycle.stream({ runId, onEvent: (event) => {
213
+ // RunLifecycle validates the run/cursor before replay callbacks. Open before enqueueing
214
+ // history so a healthy response drains replay instead of overflowing the unopened queue.
215
+ channel.open();
216
+ channel.enqueue({ event });
217
+ } }, { afterCursor });
212
218
  if (subscribed.kind !== 'ok') {
213
219
  // Nothing was ever subscribed, so `abandon()` has nothing to unsubscribe — it only marks
214
220
  // the channel closed. `res` itself is untouched, leaving `sendStreamFailure` free to send
@@ -212,11 +212,12 @@ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
212
212
  const error = guidance ? `${message}\n\n${guidance}` : message;
213
213
  return { executionId, status: 'failed', error, errorKind, ...(normalizationNotes.length ? { normalizationNotes } : {}) };
214
214
  }
215
- /** Throws the typed unknown-tool input error, naming up to eight registered prefix matches. */
216
- function assertKnownTool({ registry: invocationRegistry, toolId }) {
215
+ /** Throws the typed unknown-tool input error, naming up to eight similarity-ranked registrations.
216
+ * Input is used only for a diagnostic resource hint; this never dispatches an alternate tool. */
217
+ function assertKnownTool({ registry: invocationRegistry, toolId, input }) {
217
218
  if (invocationRegistry.has({ toolId }))
218
219
  return;
219
- const candidates = toolCandidates({ toolId, tools: invocationRegistry.list({}) });
220
+ const candidates = toolCandidates({ toolId, tools: invocationRegistry.list({}), input });
220
221
  const hint = candidates.length > 0 ? `; did you mean ${candidates.join(', ')}?` : '';
221
222
  throw new ToolInputError({ message: `ToolExecutor: unknown tool "${toolId}"${hint}` });
222
223
  }
@@ -395,7 +396,7 @@ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
395
396
  // policy that hangs or throws used to still leave a record behind. `has()` only confirms
396
397
  // existence, never returns a handler, so this doesn't reopen the leak authorizeToolInvocation
397
398
  // closed — that gate still runs, still unconditionally, inside the call below.
398
- assertKnownTool({ registry: invocationRegistry, toolId });
399
+ assertKnownTool({ registry: invocationRegistry, toolId, input });
399
400
  const executionId = openAudit(principal, run, toolId);
400
401
  // Observes `signal` for this execution's ENTIRE remaining lifetime, not just the handler-run
401
402
  // phase. `requestConfirmation()`'s wait below is unbounded — it settles only when a human
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jini-ai/daemon",
3
- "version": "0.6.10",
3
+ "version": "0.6.12",
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": {
@@ -139,17 +139,17 @@
139
139
  "@types/pg": "8.23.1",
140
140
  "express": "^4.21.0",
141
141
  "@types/express": "^4.17.21",
142
- "@jini-ai/http-kit": "0.6.10",
143
- "@jini-ai/db": "0.6.10",
144
- "@jini-ai/chat": "0.6.10",
145
- "@jini-ai/core": "^0.6.10",
146
- "@jini-ai/agent-runtime": "^0.6.10",
147
- "@jini-ai/protocol": "^0.6.10",
148
- "@jini-ai/platform": "^0.6.10"
142
+ "@jini-ai/http-kit": "0.6.12",
143
+ "@jini-ai/db": "0.6.12",
144
+ "@jini-ai/chat": "0.6.12",
145
+ "@jini-ai/agent-runtime": "^0.6.12",
146
+ "@jini-ai/platform": "^0.6.12",
147
+ "@jini-ai/protocol": "^0.6.12",
148
+ "@jini-ai/core": "^0.6.12"
149
149
  },
150
150
  "dependencies": {
151
- "@jini-ai/protocol": "^0.6.10",
152
- "@jini-ai/core": "^0.6.10"
151
+ "@jini-ai/protocol": "^0.6.12",
152
+ "@jini-ai/core": "^0.6.12"
153
153
  },
154
154
  "peerDependencies": {
155
155
  "node-pty": "^1.1.0",