@jini-ai/daemon 0.6.7 → 0.6.9

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 +8 -0
  2. package/dist/agent-executor/antigravity-permissions.d.ts +11 -0
  3. package/dist/agent-executor/antigravity-permissions.js +64 -0
  4. package/dist/agent-executor/contracts.d.ts +31 -6
  5. package/dist/agent-executor/event-translation.d.ts +6 -1
  6. package/dist/agent-executor/event-translation.js +1 -1
  7. package/dist/agent-executor/events.d.ts +1 -0
  8. package/dist/agent-executor/events.js +85 -74
  9. package/dist/agent-executor/launch.d.ts +4 -2
  10. package/dist/agent-executor/launch.js +306 -217
  11. package/dist/agent-executor/mcp-bridge-probe.js +51 -35
  12. package/dist/agent-executor/mcp-bridge.d.ts +18 -8
  13. package/dist/agent-executor/mcp-bridge.js +105 -65
  14. package/dist/agent-executor/run-output.d.ts +20 -0
  15. package/dist/agent-executor/run-output.js +80 -0
  16. package/dist/attachment-content.js +18 -15
  17. package/dist/core/read-only-tools.d.ts +26 -3
  18. package/dist/core/read-only-tools.js +38 -5
  19. package/dist/core/tool-audit.d.ts +3 -1
  20. package/dist/core/tool-audit.js +35 -22
  21. package/dist/core/tool-candidates.d.ts +8 -0
  22. package/dist/core/tool-candidates.js +6 -0
  23. package/dist/core/tool-gaps.d.ts +60 -0
  24. package/dist/core/tool-gaps.js +199 -0
  25. package/dist/delegated-tool-bridge.d.ts +1 -1
  26. package/dist/delegated-tool-bridge.js +161 -122
  27. package/dist/event-log.d.ts +45 -2
  28. package/dist/event-log.js +83 -4
  29. package/dist/http/attachments.d.ts +26 -1
  30. package/dist/http/attachments.js +129 -65
  31. package/dist/http/connectors.d.ts +1 -32
  32. package/dist/http/connectors.js +13 -68
  33. package/dist/http/delegated-tools.d.ts +5 -1
  34. package/dist/http/delegated-tools.js +29 -29
  35. package/dist/http/host-tools.d.ts +1 -0
  36. package/dist/http/host-tools.js +64 -59
  37. package/dist/http/index.d.ts +2 -2
  38. package/dist/http/index.js +1 -1
  39. package/dist/http/media.js +48 -69
  40. package/dist/http/memory.js +45 -36
  41. package/dist/http/model-proxy-fields.d.ts +23 -0
  42. package/dist/http/model-proxy-fields.js +29 -0
  43. package/dist/http/model-proxy.js +193 -395
  44. package/dist/http/research-tavily.adapter.js +72 -49
  45. package/dist/http/research.js +33 -23
  46. package/dist/http/resource-request.d.ts +37 -0
  47. package/dist/http/resource-request.js +59 -0
  48. package/dist/http/routines.js +49 -90
  49. package/dist/http/runs.d.ts +7 -5
  50. package/dist/http/runs.js +21 -7
  51. package/dist/http/terminals.js +23 -48
  52. package/dist/http/tool-catalog.d.ts +5 -0
  53. package/dist/http/xai.d.ts +11 -1
  54. package/dist/http/xai.js +125 -124
  55. package/dist/legacy-data-migration.js +41 -24
  56. package/dist/run/core/retry.js +51 -35
  57. package/dist/run/diagnostics/diagnostics.js +5 -17
  58. package/dist/run-credentials.js +49 -27
  59. package/dist/run-lifecycle.d.ts +12 -2
  60. package/dist/run-lifecycle.js +47 -26
  61. package/dist/store/agent-sessions/sqlite.d.ts +6 -0
  62. package/dist/store/agent-sessions/sqlite.js +6 -4
  63. package/dist/surface-exchanges.js +2 -1
  64. package/dist/terminal-session.d.ts +1 -1
  65. package/dist/terminal-session.js +26 -33
  66. package/dist/tool-audit.d.ts +1 -0
  67. package/dist/tool-audit.js +1 -0
  68. package/dist/tool-executor.d.ts +24 -3
  69. package/dist/tool-executor.js +277 -139
  70. package/dist/tool-recovery/recovery.js +64 -35
  71. package/dist/tool-recovery/stack.d.ts +2 -1
  72. package/dist/tool-recovery/stack.js +1 -1
  73. package/package.json +10 -10
@@ -261,26 +261,37 @@ function planRemedyCall(input) {
261
261
  return undefined;
262
262
  return { remedyToolId, carryForward, askFor };
263
263
  }
264
+ /** Prefill keys that name a secret (or a carrier of one) — never accepted as card metadata. */
265
+ const SECRET_SHAPED_PREFILL_KEY = /token|secret|password|authorization|env|headers|body|key/i;
266
+ /** Whether one prefill entry is plain card metadata: a key the setup schema declares that does not
267
+ * name a secret, holding a JSON primitive, and — for a string — a value neither the secret detector
268
+ * nor the credential-free assertion rejects. */
269
+ function isMetadataPrefillEntry({ key, value, properties, deps }) {
270
+ if (!(key in properties) || SECRET_SHAPED_PREFILL_KEY.test(key))
271
+ return false;
272
+ if (!isJsonPrimitive(value))
273
+ return false;
274
+ if (typeof value !== "string")
275
+ return true;
276
+ if (deps.containsSecret({ text: value }))
277
+ return false;
278
+ try {
279
+ deps.assertCredentialFreeField({ value, field: key }, {});
280
+ }
281
+ catch {
282
+ return false;
283
+ }
284
+ return true;
285
+ }
264
286
  /** Card schemas accept metadata only. Reject secret-shaped prefills before even asking. */
265
287
  function planCredentialSetup({ diagnostic, descriptor, deps }, _optional = {}) {
266
288
  const shape = resolveRemedySchemaShape(descriptor);
267
289
  if (!shape || diagnostic.setupToolId !== diagnostic.remedyToolId || !diagnostic.prefill)
268
290
  return undefined;
269
291
  const prefill = diagnostic.prefill;
270
- for (const [key, value] of Object.entries(prefill)) {
271
- if (!(key in shape.properties) || /token|secret|password|authorization|env|headers|body|key/i.test(key))
272
- return undefined;
273
- if (!isJsonPrimitive(value) || (typeof value === "string" && deps.containsSecret({ text: value })))
274
- return undefined;
275
- if (typeof value === "string") {
276
- try {
277
- deps.assertCredentialFreeField({ value, field: key }, {});
278
- }
279
- catch {
280
- return undefined;
281
- }
282
- }
283
- }
292
+ const allMetadata = Object.entries(prefill).every(([key, value]) => isMetadataPrefillEntry({ key, value, properties: shape.properties, deps }));
293
+ if (!allMetadata)
294
+ return undefined;
284
295
  if (shape.required.some(key => !(key in prefill)))
285
296
  return undefined;
286
297
  return { remedyToolId: diagnostic.remedyToolId, carryForward: prefill };
@@ -309,7 +320,8 @@ async function resolveRecoveryDecision(exchange, ui, askForKey) {
309
320
  return { proceed: true, answerValue };
310
321
  }
311
322
  function resolveRecoveryGate(input) {
312
- const { diagnostic, principal, originalInput, registry, result, deps } = input;
323
+ const { diagnostic, principal, originalInput, result, deps } = input;
324
+ const { registry } = deps;
313
325
  const readOnlyRefusal = refuseNonReadOnlyDispatch({ principal, toolId: diagnostic.remedyToolId, registry, messages: deps.readOnlyMessages }, {});
314
326
  if (readOnlyRefusal !== null)
315
327
  return { attempt: false, outcome: { ...result, error: deps.formatReadOnlyRemedyRefusal({ refusal: readOnlyRefusal }) } };
@@ -346,6 +358,36 @@ function buildRemedyInput(plan, decision) {
346
358
  remedyInput[plan.askFor.key] = decision.answerValue;
347
359
  return remedyInput;
348
360
  }
361
+ /**
362
+ * The remedy tool's input for an accepted gate, or `undefined` when the human did not give a usable
363
+ * go-ahead. A credential-setup diagnostic skips the recovery surface entirely: it goes directly to the
364
+ * masked card, which supplies its own human decision. Every other diagnostic asks first through the
365
+ * host-built recovery form.
366
+ */
367
+ async function resolveRemedyInput(input) {
368
+ const { plan, diagnostic, principal, emitSurface, signal, deps } = input;
369
+ // Credential setup goes directly to the masked card; it supplies its own human decision.
370
+ if (diagnostic.setupToolId)
371
+ return { ...plan.carryForward };
372
+ const exchange = deps.surfaceExchanges.open({ binding: { toolId: deps.recoveryToolId, principalId: principal.id }, emit: emitSurface });
373
+ const ui = deps.buildRecoveryForm({ exchangeId: exchange.id, hint: diagnostic.hint, remedyToolId: diagnostic.remedyToolId, ...(plan.askFor ? { askFor: plan.askFor } : {}) }, {});
374
+ const decision = await collectRecoveryDecision({ exchange, ui, askForKey: plan.askFor?.key, signal });
375
+ // Declined, dismissed, expired, or a blank answer — the ORIGINAL failure is still the truth.
376
+ if (!decision.proceed)
377
+ return undefined;
378
+ return buildRemedyInput(plan, decision);
379
+ }
380
+ /** Whether the remedy call actually applied its fix — see the two cases in the comments below. */
381
+ function remedyApplied(remedyResult) {
382
+ // The fix itself did not complete (denied, threw, timed out, ...) — nothing changed, so retrying
383
+ // the original would only reproduce the same failure.
384
+ if (remedyResult.status !== "completed")
385
+ return false;
386
+ // The remedy RAN, but its own result says the fix was never actually applied (a declined,
387
+ // expired, or invalid human-gated write) — see {@link remedyReportedFailure}. "Completed" is not
388
+ // "succeeded"; retrying here would only reproduce the same failure.
389
+ return !remedyReportedFailure(remedyResult.output);
390
+ }
349
391
  /**
350
392
  * Wraps `inner` with the generic ask -> apply -> retry-once recovery loop described in this file's
351
393
  * header. A drop-in `ToolExecutor` — every method other than `execute` delegates straight through,
@@ -378,34 +420,21 @@ export function withToolFailureRecovery({ inner, ...deps }, _optional = {}) {
378
420
  // `withReadOnlyToolConstraint` would refuse the dispatch below regardless — it is the
379
421
  // enforcement, and it does not depend on this check — but a refusal discovered THERE arrives
380
422
  // only after a human has already been asked to fill in a form whose answer is then thrown away.
381
- const gate = resolveRecoveryGate({ diagnostic, principal, originalInput: input, registry: deps.registry, result, deps });
423
+ const gate = resolveRecoveryGate({ diagnostic, principal, originalInput: input, result, deps });
382
424
  if (!gate.attempt)
383
425
  return gate.outcome;
384
- let remedyInput = { ...gate.plan.carryForward };
385
- if (!diagnostic.setupToolId) {
386
- const exchange = deps.surfaceExchanges.open({ binding: { toolId: deps.recoveryToolId, principalId: principal.id }, emit: emitSurface });
387
- const ui = deps.buildRecoveryForm({ exchangeId: exchange.id, hint: diagnostic.hint, remedyToolId: diagnostic.remedyToolId, ...(gate.plan.askFor ? { askFor: gate.plan.askFor } : {}) }, {});
388
- const decision = await collectRecoveryDecision({ exchange, ui, askForKey: gate.plan.askFor?.key, signal });
389
- // Declined, dismissed, expired, or a blank answer — the ORIGINAL failure is still the truth.
390
- if (!decision.proceed)
391
- return result;
392
- remedyInput = buildRemedyInput(gate.plan, decision);
393
- }
394
- // Credential setup goes directly to the masked card; it supplies its own human decision.
426
+ const remedyInput = await resolveRemedyInput({ plan: gate.plan, diagnostic, principal, emitSurface, signal, deps });
427
+ // Declined, dismissed, expired, or a blank answer — the ORIGINAL failure is still the truth.
428
+ if (!remedyInput)
429
+ return result;
395
430
  // ---- STRUCTURAL ONE-CYCLE GUARD ----
396
431
  // Both calls below go straight to `inner` — never back through this function or the object it
397
432
  // returns. Whatever either one returns, including a fresh hint+remedyToolId of its own, is never
398
433
  // fed back into the scan above: that scan is written exactly once, above this comment, and
399
434
  // nothing here loops or recurses into it again. See this file's header.
400
435
  const remedyResult = await inner.execute({ ...required, toolId: diagnostic.remedyToolId, input: remedyInput }, optional);
401
- // The fix itself did not complete (denied, threw, timed out, ...) — nothing changed, so retrying
402
- // the original would only reproduce the same failure. Return the ORIGINAL result, untouched.
403
- if (remedyResult.status !== "completed")
404
- return result;
405
- // The remedy RAN, but its own result says the fix was never actually applied (a declined,
406
- // expired, or invalid human-gated write) — see {@link remedyReportedFailure}. "Completed" is not
407
- // "succeeded"; retrying here would only reproduce the same failure.
408
- if (remedyReportedFailure(remedyResult.output))
436
+ // The fix was not applied (see `remedyApplied`) — return the ORIGINAL result, untouched.
437
+ if (!remedyApplied(remedyResult))
409
438
  return result;
410
439
  // Retry the ORIGINAL call exactly once, with its exact original input. Whatever this returns —
411
440
  // success or a fresh failure — is this wrapper's final answer.
@@ -41,7 +41,7 @@
41
41
  * Architectural role: `src/assistant` composition-layer adapter. Takes its collaborators as
42
42
  * arguments and constructs no policy, sink, or registry of its own.
43
43
  */
44
- import { type ToolExecutor } from "../tool-executor.js";
44
+ import { type ToolExecutor, type PrepareToolGuidance } from "../tool-executor.js";
45
45
  import type { ToolRegistry } from "@jini-ai/core";
46
46
  import type { ToolAttemptAuditSink } from "../core/tool-audit.js";
47
47
  import type { SurfaceExchangeStore } from "../surface-exchanges.js";
@@ -49,6 +49,7 @@ import { type ToolFailureRecoveryDeps } from "./recovery.js";
49
49
  import { type RedactedToolFailuresDeps } from "./redaction.js";
50
50
  export interface AssistantToolExecutorDeps extends Omit<ToolFailureRecoveryDeps, "registry"> {
51
51
  readonly registry: ToolRegistry;
52
+ readonly prepareGuidance?: PrepareToolGuidance;
52
53
  readonly surfaceExchanges: SurfaceExchangeStore;
53
54
  readonly newExecutionId: () => string;
54
55
  /** Optional sink is a real supported configuration; never require a dummy observer. */
@@ -51,7 +51,7 @@ import { withRedactedToolFailures, readToolErrorId } from "./redaction.js";
51
51
  * @complexity O(1) composition; each decorator owns its runtime cost.
52
52
  */
53
53
  export function createAssistantToolExecutor(deps, _optional = {}) {
54
- const guarded = withReadOnlyToolConstraint({ inner: createToolExecutor({ registry: deps.registry }),
54
+ const guarded = withReadOnlyToolConstraint({ inner: createToolExecutor({ registry: deps.registry }, { ...(deps.prepareGuidance ? { prepareGuidance: deps.prepareGuidance } : {}) }),
55
55
  registry: deps.registry, idGenerator: { newId: deps.newExecutionId }, messages: deps.readOnlyMessages }, {});
56
56
  const redacted = withRedactedToolFailures({ inner: guarded, ...deps.toolFailures }, {});
57
57
  const audited = deps.toolAttemptAudit
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jini-ai/daemon",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
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/agent-runtime": "^0.4.11",
143
- "@jini-ai/chat": "0.6.5",
144
- "@jini-ai/protocol": "^0.4.4",
145
- "@jini-ai/core": "^0.4.6",
146
- "@jini-ai/platform": "^0.5.8",
147
- "@jini-ai/http-kit": "0.4.2",
148
- "@jini-ai/db": "0.3.4"
142
+ "@jini-ai/db": "0.3.6",
143
+ "@jini-ai/http-kit": "0.4.3",
144
+ "@jini-ai/agent-runtime": "^0.4.12",
145
+ "@jini-ai/core": "^0.4.9",
146
+ "@jini-ai/protocol": "^0.4.5",
147
+ "@jini-ai/platform": "^0.5.10",
148
+ "@jini-ai/chat": "0.6.7"
149
149
  },
150
150
  "dependencies": {
151
- "@jini-ai/protocol": "^0.4.4",
152
- "@jini-ai/core": "^0.4.6"
151
+ "@jini-ai/core": "^0.4.9",
152
+ "@jini-ai/protocol": "^0.4.5"
153
153
  },
154
154
  "peerDependencies": {
155
155
  "node-pty": "^1.1.0",