@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.
- package/CHANGELOG.md +8 -0
- package/dist/agent-executor/antigravity-permissions.d.ts +11 -0
- package/dist/agent-executor/antigravity-permissions.js +64 -0
- package/dist/agent-executor/contracts.d.ts +31 -6
- package/dist/agent-executor/event-translation.d.ts +6 -1
- package/dist/agent-executor/event-translation.js +1 -1
- package/dist/agent-executor/events.d.ts +1 -0
- package/dist/agent-executor/events.js +85 -74
- package/dist/agent-executor/launch.d.ts +4 -2
- package/dist/agent-executor/launch.js +306 -217
- package/dist/agent-executor/mcp-bridge-probe.js +51 -35
- package/dist/agent-executor/mcp-bridge.d.ts +18 -8
- package/dist/agent-executor/mcp-bridge.js +105 -65
- package/dist/agent-executor/run-output.d.ts +20 -0
- package/dist/agent-executor/run-output.js +80 -0
- package/dist/attachment-content.js +18 -15
- package/dist/core/read-only-tools.d.ts +26 -3
- package/dist/core/read-only-tools.js +38 -5
- package/dist/core/tool-audit.d.ts +3 -1
- package/dist/core/tool-audit.js +35 -22
- package/dist/core/tool-candidates.d.ts +8 -0
- package/dist/core/tool-candidates.js +6 -0
- package/dist/core/tool-gaps.d.ts +60 -0
- package/dist/core/tool-gaps.js +199 -0
- package/dist/delegated-tool-bridge.d.ts +1 -1
- package/dist/delegated-tool-bridge.js +161 -122
- package/dist/event-log.d.ts +45 -2
- package/dist/event-log.js +83 -4
- package/dist/http/attachments.d.ts +26 -1
- package/dist/http/attachments.js +129 -65
- package/dist/http/connectors.d.ts +1 -32
- package/dist/http/connectors.js +13 -68
- package/dist/http/delegated-tools.d.ts +5 -1
- package/dist/http/delegated-tools.js +29 -29
- package/dist/http/host-tools.d.ts +1 -0
- package/dist/http/host-tools.js +64 -59
- package/dist/http/index.d.ts +2 -2
- package/dist/http/index.js +1 -1
- package/dist/http/media.js +48 -69
- package/dist/http/memory.js +45 -36
- package/dist/http/model-proxy-fields.d.ts +23 -0
- package/dist/http/model-proxy-fields.js +29 -0
- package/dist/http/model-proxy.js +193 -395
- package/dist/http/research-tavily.adapter.js +72 -49
- package/dist/http/research.js +33 -23
- package/dist/http/resource-request.d.ts +37 -0
- package/dist/http/resource-request.js +59 -0
- package/dist/http/routines.js +49 -90
- package/dist/http/runs.d.ts +7 -5
- package/dist/http/runs.js +21 -7
- package/dist/http/terminals.js +23 -48
- package/dist/http/tool-catalog.d.ts +5 -0
- package/dist/http/xai.d.ts +11 -1
- package/dist/http/xai.js +125 -124
- package/dist/legacy-data-migration.js +41 -24
- package/dist/run/core/retry.js +51 -35
- package/dist/run/diagnostics/diagnostics.js +5 -17
- package/dist/run-credentials.js +49 -27
- package/dist/run-lifecycle.d.ts +12 -2
- package/dist/run-lifecycle.js +47 -26
- package/dist/store/agent-sessions/sqlite.d.ts +6 -0
- package/dist/store/agent-sessions/sqlite.js +6 -4
- package/dist/surface-exchanges.js +2 -1
- package/dist/terminal-session.d.ts +1 -1
- package/dist/terminal-session.js +26 -33
- package/dist/tool-audit.d.ts +1 -0
- package/dist/tool-audit.js +1 -0
- package/dist/tool-executor.d.ts +24 -3
- package/dist/tool-executor.js +277 -139
- package/dist/tool-recovery/recovery.js +64 -35
- package/dist/tool-recovery/stack.d.ts +2 -1
- package/dist/tool-recovery/stack.js +1 -1
- 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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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,
|
|
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,
|
|
423
|
+
const gate = resolveRecoveryGate({ diagnostic, principal, originalInput: input, result, deps });
|
|
382
424
|
if (!gate.attempt)
|
|
383
425
|
return gate.outcome;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
|
402
|
-
|
|
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.
|
|
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/
|
|
143
|
-
"@jini-ai/
|
|
144
|
-
"@jini-ai/
|
|
145
|
-
"@jini-ai/core": "^0.4.
|
|
146
|
-
"@jini-ai/
|
|
147
|
-
"@jini-ai/
|
|
148
|
-
"@jini-ai/
|
|
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/
|
|
152
|
-
"@jini-ai/
|
|
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",
|