@jini-ai/daemon 0.6.17 → 0.6.19

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 (73) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/agent-executor/claude/mcp-config.d.ts +17 -0
  3. package/dist/agent-executor/claude/mcp-config.js +29 -0
  4. package/dist/agent-executor/codex/home-config.d.ts +7 -6
  5. package/dist/agent-executor/codex/home-config.js +15 -3
  6. package/dist/agent-executor/codex/home-staging.d.ts +4 -2
  7. package/dist/agent-executor/codex/home-staging.js +5 -3
  8. package/dist/agent-executor/contracts.d.ts +15 -0
  9. package/dist/agent-executor/event-translation.js +3 -0
  10. package/dist/agent-executor/events.js +2 -1
  11. package/dist/agent-executor/gemini/tool-calls.d.ts +7 -0
  12. package/dist/agent-executor/gemini/tool-calls.js +9 -0
  13. package/dist/agent-executor/http-mcp.d.ts +23 -0
  14. package/dist/agent-executor/http-mcp.js +74 -0
  15. package/dist/agent-executor/launch.js +43 -11
  16. package/dist/agent-executor/mcp-bridge.d.ts +10 -2
  17. package/dist/agent-executor/mcp-bridge.js +2 -2
  18. package/dist/agent-executor/opencode/mcp-config.d.ts +10 -0
  19. package/dist/agent-executor/opencode/mcp-config.js +11 -0
  20. package/dist/agent-executor/opencode/tool-calls.d.ts +7 -0
  21. package/dist/agent-executor/opencode/tool-calls.js +12 -0
  22. package/dist/agent-executor/tool-call-receipts.d.ts +7 -0
  23. package/dist/agent-executor/tool-call-receipts.js +9 -0
  24. package/dist/core/read-only-tools.d.ts +2 -0
  25. package/dist/core/read-only-tools.js +19 -3
  26. package/dist/delegated-tool-bridge.js +6 -1
  27. package/dist/http/attachments.d.ts +2 -101
  28. package/dist/http/attachments.js +13 -1
  29. package/dist/http/connectors.js +3 -1
  30. package/dist/http/db-ops.js +3 -1
  31. package/dist/http/delegated-tools.d.ts +1 -1
  32. package/dist/http/delegated-tools.js +5 -3
  33. package/dist/http/frontend-control.d.ts +0 -36
  34. package/dist/http/frontend-control.js +39 -1
  35. package/dist/http/frontend-sessions.js +4 -2
  36. package/dist/http/index.d.ts +2 -1
  37. package/dist/http/index.js +1 -0
  38. package/dist/http/media.js +3 -1
  39. package/dist/http/model-proxy.js +4 -2
  40. package/dist/http/remote-run-events.js +3 -1
  41. package/dist/http/research.js +3 -2
  42. package/dist/http/runs.js +3 -1
  43. package/dist/http/surface-restoration.d.ts +17 -0
  44. package/dist/http/surface-restoration.js +50 -0
  45. package/dist/http/terminals.js +3 -1
  46. package/dist/http/tool-catalog.d.ts +15 -1
  47. package/dist/http/tool-execution-routes.d.ts +3 -1
  48. package/dist/http/tool-execution-routes.js +4 -3
  49. package/dist/http/xai.js +3 -2
  50. package/dist/idle-monitor.d.ts +29 -0
  51. package/dist/idle-monitor.js +64 -0
  52. package/dist/image-prompt-delivery.d.ts +1 -1
  53. package/dist/image-prompt-delivery.js +4 -3
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.js +1 -0
  56. package/dist/legacy-data-migration.js +3 -1
  57. package/dist/remote-attachment-lookup.d.ts +3 -1
  58. package/dist/remote-attachment-lookup.js +2 -2
  59. package/dist/routines/scheduler.js +6 -4
  60. package/dist/routing-tool-executor.d.ts +6 -1
  61. package/dist/routing-tool-executor.js +15 -2
  62. package/dist/run-lifecycle.d.ts +0 -16
  63. package/dist/run-lifecycle.js +24 -6
  64. package/dist/surface-exchanges.d.ts +5 -1
  65. package/dist/surface-exchanges.js +41 -7
  66. package/dist/surface-restoration.d.ts +38 -0
  67. package/dist/surface-restoration.js +152 -0
  68. package/dist/tool-recovery/index.d.ts +1 -0
  69. package/dist/tool-recovery/index.js +1 -0
  70. package/dist/tool-recovery/stack.js +2 -1
  71. package/dist/tool-recovery/surface-lifetime.d.ts +11 -0
  72. package/dist/tool-recovery/surface-lifetime.js +41 -0
  73. package/package.json +14 -14
@@ -0,0 +1,38 @@
1
+ import type { Clock } from '@jini-ai/core/primitives';
2
+ import type { DeliverResult, SurfaceEmitter, SurfaceEmission, SurfaceExchangeBinding, SurfaceExchangeStore } from '@jini-ai/core';
3
+ type Lifetime = NonNullable<SurfaceEmitter['exchangeLifetime']>;
4
+ type Delivery = SurfaceExchangeStore['deliver'];
5
+ /** Shared recovery owner for every exchange channel. The store supplies delivery; the executor
6
+ * supplies retry, so recovery cannot bypass authorization or invent a second handler.
7
+ * @complexity O(n) per open/restore/accepted answer in active entries plus at most 128 retained
8
+ * entries; O(t) per emission to t observing tabs.
9
+ */
10
+ export declare function createSurfaceRestoration({ clock, defaultChannel, deliver }: {
11
+ clock: Clock;
12
+ defaultChannel: string;
13
+ deliver: Delivery;
14
+ }, _options?: Record<string, never>): {
15
+ track: ({ id, binding, lifetime }: {
16
+ id: string;
17
+ binding: SurfaceExchangeBinding;
18
+ lifetime: Lifetime;
19
+ }) => {
20
+ send: ({ emission }: {
21
+ emission: SurfaceEmission;
22
+ }) => Promise<void>;
23
+ answered: () => void;
24
+ end: () => void;
25
+ };
26
+ restore: (required: {
27
+ exchangeId: string;
28
+ principalId: string;
29
+ emit: SurfaceEmitter;
30
+ }, optional?: {
31
+ toolId?: string;
32
+ channel?: import("@jini-ai/core").SurfaceAnswerChannel;
33
+ params?: Record<string, unknown>;
34
+ signal?: AbortSignal;
35
+ }) => Promise<DeliverResult>;
36
+ };
37
+ export {};
38
+ //# sourceMappingURL=surface-restoration.d.ts.map
@@ -0,0 +1,152 @@
1
+ /** Only unanswered public invocations survive a call. Bounded in count, with no timer
2
+ * keeping a process alive. Submitted secrets never enter this cache; late answers stay on the
3
+ * submitting request's stack until they can be delivered to the newly authorized call. */
4
+ const MAX_RETAINED = 128;
5
+ const unavailable = () => ({ ok: false, reason: 'unknown-or-closed' });
6
+ /** Fan out display-only updates. Losing a tab must never replace the tool's authoritative result.
7
+ * @complexity O(t) in tabs observing this card. */
8
+ async function broadcast({ entry, emission }) {
9
+ await Promise.allSettled([...entry.observers].map(observer => observer(emission)));
10
+ }
11
+ /** Strict binding survives expiry and every retry; restoration cannot weaken ordinary delivery. */
12
+ function matches({ entry, principalId, toolId, channel, defaultChannel }) {
13
+ if (entry.binding.principalId !== principalId)
14
+ return false;
15
+ if (toolId !== undefined && entry.binding.toolId !== toolId)
16
+ return false;
17
+ return (entry.binding.channel ?? defaultChannel) === (channel ?? defaultChannel);
18
+ }
19
+ /** Subscribe until this request leaves or the shared work finishes. A departing tab cancels a
20
+ * replacement only when no other tab still observes it. It never cancels the original agent call.
21
+ * @complexity O(1) subscription state per request. */
22
+ async function join({ entry, emit, work, signal }, { initial } = {}) {
23
+ let leave;
24
+ const aborted = new Promise(resolve => { leave = () => resolve(unavailable()); });
25
+ const observer = emission => emit(emission);
26
+ entry.observers.add(observer);
27
+ signal?.addEventListener('abort', leave, { once: true });
28
+ if (signal?.aborted)
29
+ leave();
30
+ try {
31
+ if (initial)
32
+ await emit(initial);
33
+ return await Promise.race([work, aborted]);
34
+ }
35
+ finally {
36
+ entry.observers.delete(observer);
37
+ signal?.removeEventListener('abort', leave);
38
+ if (entry.observers.size === 0)
39
+ entry.restartAbort?.abort();
40
+ }
41
+ }
42
+ /** Restart once, even if several tabs return together. The old scrollback handle survives an
43
+ * interrupted replacement; a received answer consumes both it and the replacement handle.
44
+ * @complexity O(t) per emitted frame, plus the original executor's cost.
45
+ */
46
+ function restart({ entry }) {
47
+ if (entry.restarting)
48
+ return entry.restarting;
49
+ const controller = new AbortController();
50
+ entry.restartAbort = controller;
51
+ let identify;
52
+ entry.replacementReady = new Promise(resolve => { identify = resolve; });
53
+ delete entry.replacementId;
54
+ entry.restarting = Promise.resolve().then(async () => {
55
+ await entry.lifetime.retry({ signal: controller.signal, emit: async (emission) => {
56
+ entry.emission = emission;
57
+ const metadata = emission.payload['surfaceExchange'];
58
+ if (metadata?.exchangeId) {
59
+ entry.replacementId = metadata.exchangeId;
60
+ identify(metadata.exchangeId);
61
+ }
62
+ await broadcast({ entry, emission });
63
+ } });
64
+ return entry.replacementId === undefined ? unavailable() : { ok: true };
65
+ }).finally(() => {
66
+ identify(undefined); // Authorization/preparation can refuse before opening any replacement.
67
+ delete entry.restarting;
68
+ delete entry.restartAbort;
69
+ });
70
+ return entry.restarting;
71
+ }
72
+ /** Deliver a late answer only after a new, authorized exchange exists. The answer is not cached,
73
+ * reused as tool input or broadcast. A second tab cannot repeat an already consumed decision.
74
+ */
75
+ async function deliverLate({ entry, principalId, params, deliver }, options) {
76
+ const exchangeId = await entry.replacementReady;
77
+ if (exchangeId === undefined || entry.answered || options?.signal?.aborted)
78
+ return;
79
+ deliver({ exchangeId, principalId, params }, options);
80
+ }
81
+ /** Shared recovery owner for every exchange channel. The store supplies delivery; the executor
82
+ * supplies retry, so recovery cannot bypass authorization or invent a second handler.
83
+ * @complexity O(n) per open/restore/accepted answer in active entries plus at most 128 retained
84
+ * entries; O(t) per emission to t observing tabs.
85
+ */
86
+ export function createSurfaceRestoration({ clock, defaultChannel, deliver }, _options = {}) {
87
+ const entries = new Map();
88
+ function prune() {
89
+ const retained = [];
90
+ for (const [id, entry] of entries) {
91
+ if (entry.endedAt === undefined || entry.restarting)
92
+ continue;
93
+ retained.push(id);
94
+ }
95
+ for (const id of retained.slice(0, Math.max(0, retained.length - MAX_RETAINED)))
96
+ entries.delete(id);
97
+ }
98
+ function forgetAnswered({ id }) {
99
+ for (const [key, entry] of entries) {
100
+ if (key === id || entry.replacementId === id) {
101
+ entry.answered = true;
102
+ entries.delete(key);
103
+ }
104
+ }
105
+ }
106
+ function track({ id, binding, lifetime }) {
107
+ prune();
108
+ let finish;
109
+ const ended = new Promise(resolve => { finish = resolve; });
110
+ const entry = { binding: { ...binding }, lifetime, answered: false, observers: new Set(), finish, ended };
111
+ entries.set(id, entry);
112
+ return {
113
+ send: async ({ emission }) => {
114
+ entry.emission = emission;
115
+ await broadcast({ entry, emission });
116
+ },
117
+ answered: () => forgetAnswered({ id }),
118
+ end: () => {
119
+ entry.endedAt = clock.nowMs();
120
+ entry.finish();
121
+ // A synthetic/nested card cannot safely replay its outer tool. No emitted surface means
122
+ // nobody saw a card to return to; retaining that call would be speculative work.
123
+ if (!entry.emission || entry.answered || binding.toolId !== lifetime.toolId)
124
+ entries.delete(id);
125
+ prune();
126
+ },
127
+ };
128
+ }
129
+ const restore = async ({ exchangeId, principalId, emit }, options = {}) => {
130
+ prune();
131
+ const entry = entries.get(exchangeId);
132
+ if (!entry)
133
+ return unavailable();
134
+ if (!matches({ entry, principalId, toolId: options.toolId, channel: options.channel, defaultChannel }))
135
+ return { ok: false, reason: 'binding-mismatch' };
136
+ if (options.signal?.aborted)
137
+ return unavailable();
138
+ if (entry.endedAt === undefined) {
139
+ if (options.params)
140
+ return deliver({ exchangeId, principalId, params: options.params }, options);
141
+ return join({ entry, emit, work: entry.ended.then(() => ({ ok: true })), signal: options.signal }, entry.emission ? { initial: entry.emission } : {});
142
+ }
143
+ const work = restart({ entry });
144
+ // Subscribe synchronously before the retry's microtask can emit the first card.
145
+ const observed = join({ entry, emit, work, signal: options.signal }, entry.replacementId && entry.emission ? { initial: entry.emission } : {});
146
+ if (options.params)
147
+ await deliverLate({ entry, principalId, params: options.params, deliver }, options);
148
+ return observed;
149
+ };
150
+ return { track, restore };
151
+ }
152
+ //# sourceMappingURL=surface-restoration.js.map
@@ -5,4 +5,5 @@ export * from "./stack.js";
5
5
  export * from "./federated-refusal.js";
6
6
  export * from "./observability.js";
7
7
  export * from "./analytics.js";
8
+ export * from './surface-lifetime.js';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -5,4 +5,5 @@ export * from "./stack.js";
5
5
  export * from "./federated-refusal.js";
6
6
  export * from "./observability.js";
7
7
  export * from "./analytics.js";
8
+ export * from './surface-lifetime.js';
8
9
  //# sourceMappingURL=index.js.map
@@ -46,6 +46,7 @@ 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
+ import { withSurfaceExchangeLifetime } from './surface-lifetime.js';
49
50
  /** The audit wrapper's options: only an error sink the caller actually supplied. */
50
51
  function auditOptions(audit) {
51
52
  return audit.onSinkError ? { onSinkError: audit.onSinkError } : {};
@@ -61,6 +62,6 @@ export function createAssistantToolExecutor(deps, _optional = {}) {
61
62
  const audited = deps.toolAttemptAudit
62
63
  ? withToolAttemptAudit({ inner: redacted, ...deps.toolAttemptAudit, readErrorId: readToolErrorId }, auditOptions(deps.toolAttemptAudit))
63
64
  : redacted;
64
- return withToolFailureRecovery({ ...deps, inner: audited }, {});
65
+ return withSurfaceExchangeLifetime({ inner: withToolFailureRecovery({ ...deps, inner: audited }, {}) });
65
66
  }
66
67
  //# sourceMappingURL=stack.js.map
@@ -0,0 +1,11 @@
1
+ import type { ToolExecutor } from '../tool-executor.js';
2
+ /** Bind every card type to the executing call, and make restoration re-enter this same stack.
3
+ * No submitted answer is captured here: input is the original model-supplied invocation only.
4
+ * @param required.inner The complete authorization, recovery and audit stack.
5
+ * @returns An executor with the same cancellation and audit ports.
6
+ * @complexity O(input size) to snapshot public arguments; O(1) lifecycle state per call.
7
+ */
8
+ export declare function withSurfaceExchangeLifetime({ inner }: {
9
+ inner: ToolExecutor;
10
+ }, _options?: Record<string, never>): ToolExecutor;
11
+ //# sourceMappingURL=surface-lifetime.d.ts.map
@@ -0,0 +1,41 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ /** Bind every card type to the executing call, and make restoration re-enter this same stack.
3
+ * No submitted answer is captured here: input is the original model-supplied invocation only.
4
+ * @param required.inner The complete authorization, recovery and audit stack.
5
+ * @returns An executor with the same cancellation and audit ports.
6
+ * @complexity O(input size) to snapshot public arguments; O(1) lifecycle state per call.
7
+ */
8
+ export function withSurfaceExchangeLifetime({ inner }, _options = {}) {
9
+ const execute = async (required, options = {}) => {
10
+ if (!options.emitSurface)
11
+ return inner.execute(required, options);
12
+ const invocation = { ...required, principal: structuredClone(required.principal),
13
+ run: { ...required.run }, input: structuredClone(required.input) };
14
+ // Remote execution adapters establish trusted site/run context around this call. A later
15
+ // authenticated answer arrives in another async chain; restore that context before re-gating.
16
+ const withinInvocation = AsyncLocalStorage.snapshot();
17
+ const controller = new AbortController();
18
+ const abort = () => controller.abort();
19
+ options.signal?.addEventListener('abort', abort, { once: true });
20
+ if (options.signal?.aborted)
21
+ abort();
22
+ const emit = Object.assign((emission) => options.emitSurface(emission), { exchangeLifetime: {
23
+ signal: controller.signal, runId: required.run.id, toolId: required.toolId, principalId: required.principal.id,
24
+ retry: async ({ emit: replacement, signal }) => {
25
+ await withinInvocation(() => execute({ ...invocation, input: structuredClone(invocation.input) }, {
26
+ ...options, signal, emitSurface: replacement,
27
+ }));
28
+ },
29
+ } });
30
+ try {
31
+ return await inner.execute(required, { ...options, signal: controller.signal, emitSurface: emit });
32
+ }
33
+ finally {
34
+ options.signal?.removeEventListener('abort', abort);
35
+ controller.abort();
36
+ }
37
+ };
38
+ return { execute, cancel: inner.cancel.bind(inner), resumeConfirmation: inner.resumeConfirmation.bind(inner),
39
+ getAuditRecord: inner.getAuditRecord.bind(inner) };
40
+ }
41
+ //# sourceMappingURL=surface-lifetime.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jini-ai/daemon",
3
- "version": "0.6.17",
3
+ "version": "0.6.19",
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": {
@@ -152,23 +152,23 @@
152
152
  "@types/pg": "8.23.1",
153
153
  "express": "^4.21.0",
154
154
  "@types/express": "^4.17.21",
155
- "@jini-ai/http-kit": "0.6.17",
156
- "@jini-ai/chat": "0.6.17",
157
- "@jini-ai/db": "0.6.17",
158
- "@jini-ai/core": "^0.6.17",
159
- "@jini-ai/agent-runtime": "^0.6.17",
160
- "@jini-ai/platform": "^0.6.17",
161
- "@jini-ai/protocol": "^0.6.17"
155
+ "@jini-ai/db": "0.6.19",
156
+ "@jini-ai/agent-runtime": "^0.6.19",
157
+ "@jini-ai/chat": "0.6.19",
158
+ "@jini-ai/core": "^0.6.19",
159
+ "@jini-ai/platform": "^0.6.19",
160
+ "@jini-ai/http-kit": "0.6.19",
161
+ "@jini-ai/protocol": "^0.6.19"
162
162
  },
163
163
  "dependencies": {
164
164
  "smol-toml": "1.9.1",
165
165
  "kysely": "^0.29.6",
166
- "@jini-ai/core": "^0.6.17",
167
- "@jini-ai/db": "^0.6.17",
168
- "@jini-ai/platform": "^0.6.17",
169
- "@jini-ai/agent-runtime": "^0.6.17",
170
- "@jini-ai/protocol": "^0.6.17",
171
- "@jini-ai/http-kit": "^0.6.17"
166
+ "@jini-ai/core": "^0.6.19",
167
+ "@jini-ai/protocol": "^0.6.19",
168
+ "@jini-ai/agent-runtime": "^0.6.19",
169
+ "@jini-ai/db": "^0.6.19",
170
+ "@jini-ai/platform": "^0.6.19",
171
+ "@jini-ai/http-kit": "^0.6.19"
172
172
  },
173
173
  "peerDependencies": {
174
174
  "node-pty": "^1.1.0",