@jini-ai/daemon 0.5.2 → 0.5.4
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 +11 -0
- package/dist/agent-executor/contracts.d.ts +26 -20
- package/dist/agent-executor/contracts.js +0 -9
- package/dist/agent-executor/event-translation.d.ts +2 -2
- package/dist/agent-executor/event-translation.js +1 -2
- package/dist/agent-executor/events.d.ts +9 -4
- package/dist/agent-executor/events.js +29 -7
- package/dist/agent-executor/handoff.d.ts +1 -1
- package/dist/agent-executor/handoff.js +0 -1
- package/dist/agent-executor/launch.d.ts +1 -1
- package/dist/agent-executor/launch.js +24 -1
- package/dist/agent-executor/mcp-bridge.d.ts +2 -2
- package/dist/agent-executor/mcp-bridge.js +0 -2
- package/dist/agent-executor/model-receipts.d.ts +6 -0
- package/dist/agent-executor/model-receipts.js +26 -0
- package/dist/agent-executor.d.ts +8 -1663
- package/dist/agent-executor.js +8 -3005
- package/dist/attachment-content.d.ts +45 -0
- package/dist/attachment-content.js +40 -0
- package/dist/core/read-only-tools.d.ts +130 -0
- package/dist/core/read-only-tools.js +115 -0
- package/dist/core/tool-audit.d.ts +131 -0
- package/dist/core/tool-audit.js +113 -0
- package/dist/http/active-context.d.ts +2 -2
- package/dist/http/agents.d.ts +8 -2
- package/dist/http/agents.js +6 -2
- package/dist/http/attachments.d.ts +16 -7
- package/dist/http/attachments.js +3 -2
- package/dist/http/component-catalog.d.ts +2 -2
- package/dist/http/connectors.d.ts +17 -17
- package/dist/http/daemon-status.d.ts +2 -2
- package/dist/http/db-ops.d.ts +3 -3
- package/dist/http/delegated-tools.d.ts +1 -1
- package/dist/http/delegated-tools.js +1 -1
- package/dist/http/frontend-sessions.d.ts +1 -1
- package/dist/http/health.d.ts +6 -6
- package/dist/http/host-tools.d.ts +2 -2
- package/dist/http/media.d.ts +4 -4
- package/dist/http/memory.d.ts +15 -15
- package/dist/http/remote-run-events.d.ts +2 -2
- package/dist/http/research.d.ts +1 -1
- package/dist/http/routines.d.ts +7 -7
- package/dist/http/run-credentials.d.ts +2 -2
- package/dist/http/run-credentials.js +2 -2
- package/dist/http/runs.d.ts +4 -4
- package/dist/http/terminals.d.ts +6 -6
- package/dist/http/tool-catalog.d.ts +2 -2
- package/dist/http/xai.d.ts +6 -6
- package/dist/index.d.ts +76 -571
- package/dist/index.js +76 -571
- package/dist/read-only-tools.d.ts +1 -129
- package/dist/read-only-tools.js +2 -114
- package/dist/run-credentials.d.ts +47 -23
- package/dist/run-credentials.js +24 -8
- package/dist/run-lifecycle.d.ts +1 -1
- package/dist/run-lifecycle.js +6 -5
- package/dist/scheduler.d.ts +5 -0
- package/dist/scheduler.js +13 -1
- package/dist/session-coordination.d.ts +58 -24
- package/dist/session-coordination.js +40 -12
- package/dist/store/event-log/sqlite.js +3 -3
- package/dist/surface-exchanges.d.ts +207 -61
- package/dist/surface-exchanges.js +167 -20
- package/dist/tool-audit.d.ts +1 -113
- package/dist/tool-audit.js +2 -94
- package/dist/tool-recovery/diagnostics.d.ts +132 -0
- package/dist/tool-recovery/diagnostics.js +110 -0
- package/dist/tool-recovery/federated-refusal.d.ts +92 -0
- package/dist/tool-recovery/federated-refusal.js +152 -0
- package/dist/tool-recovery/index.d.ts +6 -0
- package/dist/tool-recovery/index.js +6 -0
- package/dist/tool-recovery/recovery.d.ts +49 -0
- package/dist/tool-recovery/recovery.js +419 -0
- package/dist/tool-recovery/redaction.d.ts +166 -0
- package/dist/tool-recovery/redaction.js +192 -0
- package/dist/tool-recovery/stack.d.ts +69 -0
- package/dist/tool-recovery/stack.js +62 -0
- package/package.json +35 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## 0.5.4 — pending release
|
|
2
|
+
|
|
3
|
+
- Re-export the canonical core surface-exchange contracts and parameter constants. Store and ask/report behavior are unchanged; requires core 0.4.2.
|
|
4
|
+
|
|
5
|
+
## 0.5.3 — 2026-10-06
|
|
6
|
+
|
|
7
|
+
- Message attachments reach every runtime: new `prepareMessageAttachments` / `messageContentWithImages` (+ `MessageAttachmentImage`, `MessageAttachmentSource`, `MessageAttachmentReader`) turn claimed refs into image pixels for providers and a file notice for non-images.
|
|
8
|
+
- `AttachmentStore.listPendingForOwner({ ownerId }, { runId })`: with the claiming `runId`, that run's own claimed attachments stay discoverable (owner match still required); without it, behavior is unchanged.
|
|
9
|
+
- The agent executor reports the CLI session id on an early `status` event so hosts can checkpoint a run before it ends; run lifecycle carries it through.
|
|
10
|
+
- Requires `@jini-ai/protocol` ^0.4.1 and `@jini-ai/agent-runtime` ^0.4.1.
|
|
11
|
+
|
|
1
12
|
## 0.5.2 — 2026-10-06
|
|
2
13
|
|
|
3
14
|
- No slow-run notice while a delegated tool waits on a surface: the first `emitSurface` of a call suspends the notice and the call's end resumes it. Suspensions are counted, so with two calls in flight the first to finish does not re-arm the notice over the other's open form; `resume()` drops suspensions left by an ended run.
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
1
|
+
import type { MessageAttachmentImage } from '../attachment-content.js';
|
|
2
|
+
import type { spawn as nodeSpawn, ChildProcess } from 'node:child_process';
|
|
3
|
+
import type { Principal } from '@jini-ai/core';
|
|
4
|
+
import type { RunAgentPayload, RunErrorPayload } from '@jini-ai/protocol';
|
|
5
|
+
import type { applyAgentLaunchEnv, ensureAgentCapabilities, resolveModelForLaunch, getAgentDef, resolveAgentLaunch, attachAcpSession, attachPiRpcSession, prepareAgentLogFile, preparePromptFileForAgent, AcpMcpServerInput, AcpPermissionHandler, PromptAugmenter } from '@jini-ai/agent-runtime';
|
|
6
|
+
import type { createCommandInvocation, listProcessSnapshots, ProcessSnapshot, StopProcessesResult } from '@jini-ai/platform';
|
|
7
|
+
import type { RunByteJournal } from '../continuation/journal.js';
|
|
8
|
+
import type { RunRetrySideEffectState } from '../run/core/index.js';
|
|
9
|
+
import type { ToolExecutor } from '../tool-executor.js';
|
|
10
|
+
import type { RunLifecycle } from '../run-lifecycle.js';
|
|
10
11
|
/**
|
|
11
12
|
* A parsed, loosely-typed event as produced by one of `@jini-ai/agent-runtime`'s
|
|
12
13
|
* four stream-parser factories — each parser's `onEvent` callback receives
|
|
@@ -55,15 +56,13 @@ export type AgentExecutorCompatibility = {
|
|
|
55
56
|
* produce plus defensively malformed/non-record input.
|
|
56
57
|
*
|
|
57
58
|
* `'agent'`'s optional `sessionId` (gap 5, session resume — see
|
|
58
|
-
* `RunEndPayload.sessionRef`'s doc in `@jini-ai/protocol`)
|
|
59
|
-
* side channel
|
|
60
|
-
*
|
|
61
|
-
* `
|
|
62
|
-
*
|
|
63
|
-
* `
|
|
64
|
-
*
|
|
65
|
-
* thread it into its own terminal `finish()` call, without widening the
|
|
66
|
-
* public wire protocol just to carry a value that only this module reads.
|
|
59
|
+
* `RunEndPayload.sessionRef`'s doc in `@jini-ai/protocol`) remains a daemon-internal
|
|
60
|
+
* side channel for lifecycle wiring to capture the locator and thread it into
|
|
61
|
+
* its terminal `finish()` call. The `'status'` wire payload also carries
|
|
62
|
+
* `sessionId` so durable hosts can persist it immediately, before a process
|
|
63
|
+
* crash can prevent the terminal `end` event from arriving. OpenCode's
|
|
64
|
+
* `sessionID`/Codex's `thread_id`/Qoder's and Claude's `session_id` are normalized
|
|
65
|
+
* by their stream parsers into this shared locator.
|
|
67
66
|
*
|
|
68
67
|
* `'turn-end'`'s optional `stopReason` (gap 3, capability-routed
|
|
69
68
|
* continuation transport) is the same kind of internal side channel: the
|
|
@@ -91,7 +90,7 @@ export type AgentRuntimeEventTranslation = {
|
|
|
91
90
|
readonly kind: 'ignored';
|
|
92
91
|
};
|
|
93
92
|
/** Machine-readable failure reasons `run()` can reject with — every one is preceded by a `lifecycle.finish({status:'failed'})` call (see index.ts module doc's Invariant section). */
|
|
94
|
-
export type AgentExecutorErrorCode = 'AGENT_PERMISSION_MODE_INVALID' | 'AGENT_NOT_FOUND' | 'AGENT_RUNTIME_UNSUPPORTED' | 'AGENT_BINARY_NOT_RESOLVED' | 'AGENT_SPAWN_FAILED' | 'AGENT_PROMPT_TOO_LARGE';
|
|
93
|
+
export type AgentExecutorErrorCode = 'AGENT_MODEL_UNRESOLVED' | 'AGENT_PERMISSION_MODE_INVALID' | 'AGENT_NOT_FOUND' | 'AGENT_RUNTIME_UNSUPPORTED' | 'AGENT_BINARY_NOT_RESOLVED' | 'AGENT_SPAWN_FAILED' | 'AGENT_PROMPT_TOO_LARGE';
|
|
95
94
|
/** Thrown by `AgentExecutor.run()` on every failure path — never a bare `Error`, so callers can branch on `.code` instead of parsing `.message`. */
|
|
96
95
|
export declare class AgentExecutorError extends Error {
|
|
97
96
|
readonly code: AgentExecutorErrorCode;
|
|
@@ -129,6 +128,8 @@ export interface AgentExecutorRunInput {
|
|
|
129
128
|
* the top of `run()` in `launch.ts`.
|
|
130
129
|
*/
|
|
131
130
|
readonly imagePaths?: readonly string[];
|
|
131
|
+
/** Host-prepared pixels from the shared attachment reader, for structured message delivery. */
|
|
132
|
+
readonly imageContents?: readonly MessageAttachmentImage[];
|
|
132
133
|
/** Additional host-validated directories the runtime may read. */
|
|
133
134
|
readonly extraAllowedDirs?: readonly string[];
|
|
134
135
|
/** Trusted root that must contain pi-rpc image paths after realpath resolution. */
|
|
@@ -192,7 +193,7 @@ export interface AgentExecutor {
|
|
|
192
193
|
* underlying run is always already transitioned to `'failed'` via
|
|
193
194
|
* `lifecycle.finish()` before this rejects (see index.ts module doc's Invariant).
|
|
194
195
|
*/
|
|
195
|
-
run(requiredArgs: Pick<AgentExecutorRunInput, "runId" | "agentId" | "prompt" | "cwd">, optionalArgs?: Pick<AgentExecutorRunInput, "model" | "reasoning" | "permissionMode" | "imagePaths" | "extraAllowedDirs" | "uploadRoot" | "credentialEnv" | "env" | "resumeSessionId" | "newSessionId" | "disallowedTools" | "allowedTools" | "settingSources" | "settings">): Promise<void>;
|
|
196
|
+
run(requiredArgs: Pick<AgentExecutorRunInput, "runId" | "agentId" | "prompt" | "cwd">, optionalArgs?: Pick<AgentExecutorRunInput, "model" | "reasoning" | "permissionMode" | "imagePaths" | "imageContents" | "extraAllowedDirs" | "uploadRoot" | "credentialEnv" | "env" | "resumeSessionId" | "newSessionId" | "disallowedTools" | "allowedTools" | "settingSources" | "settings">): Promise<void>;
|
|
196
197
|
}
|
|
197
198
|
/** Process discovery is a getter; process collection and stopping accept explicit host inputs. */
|
|
198
199
|
export type CollectProcessTreePidsPort = (args: {
|
|
@@ -230,6 +231,9 @@ export interface AgentCleanupFailureContext {
|
|
|
230
231
|
}
|
|
231
232
|
/** A small handle `writePromptToStdin` uses to close stdin exactly once, shared with the `turn_end`-triggered close inside {@link wireChildLifecycle}. */
|
|
232
233
|
export interface StdinCloseHandle {
|
|
234
|
+
/** Prepared pixels ride on the initial stdin message without reopening filesystem tools or
|
|
235
|
+
* serializing base64 into text. Native argv/ACP/pi transports keep their existing image path. */
|
|
236
|
+
imageContents?: readonly MessageAttachmentImage[];
|
|
233
237
|
closeStdinOnce(): void;
|
|
234
238
|
/** Journals a sent-to-stdin byte chunk, queued through the same FIFO {@link wireChildLifecycle} already uses for emitted events. No-op when no journal was configured (see `CreateAgentExecutorOptions.journal`). */
|
|
235
239
|
recordSentBytes(content: string): void;
|
|
@@ -539,6 +543,8 @@ export interface FailureClassificationContext {
|
|
|
539
543
|
export type ClassifyFailure = (context: FailureClassificationContext) => boolean | Promise<boolean>;
|
|
540
544
|
export interface CreateAgentExecutorOptions {
|
|
541
545
|
readonly lifecycle: RunLifecycle;
|
|
546
|
+
/** Resolves/pins the concrete starting model against the actual launch scope. */
|
|
547
|
+
readonly resolveModelForLaunch?: typeof resolveModelForLaunch;
|
|
542
548
|
/** @default the real `@jini-ai/agent-runtime` registry lookup */
|
|
543
549
|
readonly getAgentDef?: typeof getAgentDef;
|
|
544
550
|
/** @default the real `@jini-ai/agent-runtime` launch resolver */
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
import {} from 'node:child_process';
|
|
2
|
-
import {} from '@jini-ai/core';
|
|
3
|
-
import {} from '@jini-ai/protocol';
|
|
4
|
-
import {} from '@jini-ai/agent-runtime';
|
|
5
|
-
import {} from '@jini-ai/platform';
|
|
6
|
-
import {} from '../continuation/journal.js';
|
|
7
|
-
import {} from '../run/core/index.js';
|
|
8
|
-
import {} from '../tool-executor.js';
|
|
9
|
-
import {} from '../run-lifecycle.js';
|
|
10
1
|
export const SUPPORTED_STREAM_FORMATS = [
|
|
11
2
|
'claude-stream-json',
|
|
12
3
|
'json-event-stream',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { RunAgentPayload, RunErrorPayload } from '@jini-ai/protocol';
|
|
2
|
+
import type { AgentRuntimeEventTranslation } from './contracts.js';
|
|
3
3
|
/**
|
|
4
4
|
* Narrows a parsed `usage` event's `usage` sub-object (`{input_tokens?, output_tokens?}`) — the one
|
|
5
5
|
* piece of {@link translateUsagePayload} with real nested branching (an optional container holding
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import {} from '@jini-ai/protocol';
|
|
2
|
-
import {} from './contracts.js';
|
|
3
1
|
import { isRecord, asString, asOptionalString, } from './values.js';
|
|
4
2
|
function asOptionalNumber(value) {
|
|
5
3
|
return typeof value === 'number' ? value : undefined;
|
|
@@ -62,6 +60,7 @@ export function translateStatusEvent({ rawEvent }) {
|
|
|
62
60
|
...(model !== undefined ? { model } : {}),
|
|
63
61
|
...(ttftMs !== undefined ? { ttftMs } : {}),
|
|
64
62
|
...(detail !== undefined ? { detail } : {}),
|
|
63
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
65
64
|
},
|
|
66
65
|
...(sessionId !== undefined ? { sessionId } : {}),
|
|
67
66
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ModelCatalogSnapshot } from '@jini-ai/agent-runtime';
|
|
2
|
+
import type { ChildProcess } from 'node:child_process';
|
|
2
3
|
import { attachAcpSession, attachPiRpcSession, type AcpPermissionHandler, type RuntimeAgentDef } from '@jini-ai/agent-runtime';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import type { RunByteJournal } from '../continuation/journal.js';
|
|
5
|
+
import type { RunLifecycle } from '../run-lifecycle.js';
|
|
6
|
+
import type { ChildDrivenStreamFormat, TerminateChildTreeDeps, AgentCleanupFailureContext, StdinCloseHandle, ContinuationOptions, McpBridgeDelivery, ClassifyFailure, FailBeforeSpawn } from './contracts.js';
|
|
6
7
|
/** Default sink when a host does not supply `onCleanupFailure`: still observable, never silent. Redacted per SEC-007 (a spawn/permission error can embed paths/host detail). */
|
|
7
8
|
export declare function defaultCleanupFailureSink(context: AgentCleanupFailureContext): void;
|
|
8
9
|
/**
|
|
@@ -22,6 +23,8 @@ export declare function defaultCleanupFailureSink(context: AgentCleanupFailureCo
|
|
|
22
23
|
*/
|
|
23
24
|
export declare const DEFAULT_BUFFERED_STDOUT_MAX_BYTES: number;
|
|
24
25
|
interface WireChildLifecycleContext extends TerminateChildTreeDeps {
|
|
26
|
+
readonly startingModel?: string;
|
|
27
|
+
readonly modelCatalog?: ModelCatalogSnapshot;
|
|
25
28
|
readonly runId: string;
|
|
26
29
|
readonly def: RuntimeAgentDef;
|
|
27
30
|
readonly streamFormat: ChildDrivenStreamFormat;
|
|
@@ -141,6 +144,7 @@ interface RunAcpDispatchInput {
|
|
|
141
144
|
readonly prompt: string;
|
|
142
145
|
readonly cwd: string;
|
|
143
146
|
readonly model: string | undefined;
|
|
147
|
+
readonly modelCatalog?: ModelCatalogSnapshot;
|
|
144
148
|
readonly imagePaths: readonly string[];
|
|
145
149
|
readonly envFormat: 'array' | 'map' | undefined;
|
|
146
150
|
readonly mcpBridge: McpBridgeDelivery | null;
|
|
@@ -168,6 +172,7 @@ interface RunPiRpcDispatchInput {
|
|
|
168
172
|
readonly prompt: string;
|
|
169
173
|
readonly cwd: string;
|
|
170
174
|
readonly model: string | undefined;
|
|
175
|
+
readonly modelCatalog?: ModelCatalogSnapshot;
|
|
171
176
|
readonly imagePaths: readonly string[];
|
|
172
177
|
readonly uploadRoot: string | undefined;
|
|
173
178
|
}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import {} from '
|
|
1
|
+
import { createModelReceiptTracker } from './model-receipts.js';
|
|
2
|
+
import { messageContentWithImages } from '../attachment-content.js';
|
|
2
3
|
import { redactSecrets, } from '@jini-ai/core';
|
|
3
|
-
import {} from '@jini-ai/protocol';
|
|
4
4
|
import { createClaudeStreamHandler, createCopilotStreamHandler, createJsonEventStreamHandler, createQoderStreamHandler, attachAcpSession, attachPiRpcSession, } from '@jini-ai/agent-runtime';
|
|
5
5
|
import { collectProcessTreePids, listProcessSnapshots, stopProcesses, } from '@jini-ai/platform';
|
|
6
6
|
import { classifyRunCloseStatus, } from '../close-status.js';
|
|
7
7
|
import { resolveContinuationTransport, } from '../continuation/continuation-transport.js';
|
|
8
|
-
import {} from '../continuation/journal.js';
|
|
9
8
|
import { resultContent, } from '../delegated-tool-bridge.js';
|
|
10
9
|
import { extractResultMedia, } from '../tool-result-media.js';
|
|
11
|
-
import {} from '../run-lifecycle.js';
|
|
12
|
-
import {} from './contracts.js';
|
|
13
10
|
import { translateAgentRuntimeEvent, translateAcpError, applyAgentTranslationSideEffects, } from './event-translation.js';
|
|
14
11
|
import { errorMessage, } from './values.js';
|
|
15
12
|
import { MCP_BRIDGE_UNAVAILABLE, unavailableJiniBridgeStatus, bridgeUnavailableMessage, } from './mcp-bridge.js';
|
|
13
|
+
/** Carry PID only on structured status so the host can record its OS start identity early.
|
|
14
|
+
* A PID alone is never proof of death; hosts verify PID reuse before native continuation. */
|
|
15
|
+
function statusWithChildIdentity({ payload, childPid }, _optional = {}) {
|
|
16
|
+
return { ...payload, ...(payload.type === 'status' && childPid !== undefined ? { childPid } : {}) };
|
|
17
|
+
}
|
|
16
18
|
/**
|
|
17
19
|
* Selects and constructs the real stream-parser handler for a supported
|
|
18
20
|
* `streamFormat`. `json-event-stream` additionally dispatches on
|
|
@@ -254,6 +256,7 @@ function bufferedStdoutTruncationNotice(droppedBytes, maxBytes) {
|
|
|
254
256
|
*/
|
|
255
257
|
export function wireChildLifecycle(ctx) {
|
|
256
258
|
const { runId, def, streamFormat, child, lifecycle, journal, continuation, classifyFailure } = ctx;
|
|
259
|
+
const modelReceipt = createModelReceiptTracker(ctx.startingModel, ctx.modelCatalog?.models);
|
|
257
260
|
let stdinClosed = false;
|
|
258
261
|
let cancelRequested = false;
|
|
259
262
|
let emitQueue = Promise.resolve();
|
|
@@ -300,6 +303,8 @@ export function wireChildLifecycle(ctx) {
|
|
|
300
303
|
}
|
|
301
304
|
});
|
|
302
305
|
}
|
|
306
|
+
if (ctx.startingModel)
|
|
307
|
+
enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: { type: 'status', label: 'starting_model', model: ctx.startingModel } } }));
|
|
303
308
|
function closeStdinOnce() {
|
|
304
309
|
if (stdinClosed)
|
|
305
310
|
return;
|
|
@@ -393,6 +398,9 @@ export function wireChildLifecycle(ctx) {
|
|
|
393
398
|
return;
|
|
394
399
|
const translation = translateAgentRuntimeEvent({ rawEvent: rawEvent });
|
|
395
400
|
if (translation.kind === 'agent') {
|
|
401
|
+
const receipt = modelReceipt(translation.payload);
|
|
402
|
+
if (receipt)
|
|
403
|
+
enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: receipt } }));
|
|
396
404
|
if (translation.sessionId !== undefined)
|
|
397
405
|
capturedSessionId = translation.sessionId;
|
|
398
406
|
if (!bridgeChecked && translation.payload.type === 'status' && translation.payload.label === 'initializing') {
|
|
@@ -411,7 +419,7 @@ export function wireChildLifecycle(ctx) {
|
|
|
411
419
|
translation.payload.delta.length > 0) {
|
|
412
420
|
userVisibleOutputSeen = true;
|
|
413
421
|
}
|
|
414
|
-
enqueueEmit(() => lifecycle.emit({ runId: runId, input: { event: 'agent', data: translation.payload } }));
|
|
422
|
+
enqueueEmit(() => lifecycle.emit({ runId: runId, input: { event: 'agent', data: statusWithChildIdentity({ payload: translation.payload, childPid: ctx.child.pid }, {}) } }));
|
|
415
423
|
}
|
|
416
424
|
else if (translation.kind === 'error') {
|
|
417
425
|
enqueueEmit(() => lifecycle.emit({ runId: runId, input: { event: 'error', data: translation.payload } }));
|
|
@@ -564,6 +572,7 @@ export function wireChildLifecycle(ctx) {
|
|
|
564
572
|
* success.
|
|
565
573
|
*/
|
|
566
574
|
function wireAcpLifecycle(ctx) {
|
|
575
|
+
const modelReceipt = createModelReceiptTracker(ctx.model, ctx.modelCatalog?.models);
|
|
567
576
|
const { runId, agentId, child, lifecycle, journal, classifyFailure } = ctx;
|
|
568
577
|
let cancelRequested = false;
|
|
569
578
|
let emitQueue = Promise.resolve();
|
|
@@ -583,6 +592,8 @@ function wireAcpLifecycle(ctx) {
|
|
|
583
592
|
}
|
|
584
593
|
});
|
|
585
594
|
}
|
|
595
|
+
if (ctx.model)
|
|
596
|
+
enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: { type: 'status', label: 'starting_model', model: ctx.model } } }));
|
|
586
597
|
child.stdout?.on('data', (chunk) => {
|
|
587
598
|
const text = chunk.toString('utf8');
|
|
588
599
|
if (journal)
|
|
@@ -626,6 +637,9 @@ function wireAcpLifecycle(ctx) {
|
|
|
626
637
|
if (event === 'agent') {
|
|
627
638
|
const translation = translateAgentRuntimeEvent({ rawEvent: payload });
|
|
628
639
|
if (translation.kind === 'agent') {
|
|
640
|
+
const receipt = modelReceipt(translation.payload);
|
|
641
|
+
if (receipt)
|
|
642
|
+
enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: receipt } }));
|
|
629
643
|
applyAgentTranslationSideEffects({ payload: translation.payload, sessionId: translation.sessionId, sink: {
|
|
630
644
|
onSessionId: ({ sessionId }) => { capturedSessionId = sessionId; },
|
|
631
645
|
onToolCall: () => { toolCallSeen = true; },
|
|
@@ -668,6 +682,7 @@ function wireAcpLifecycle(ctx) {
|
|
|
668
682
|
* multi-turn tool continuation, resumable session ids, etc.).
|
|
669
683
|
*/
|
|
670
684
|
function wirePiRpcLifecycle(ctx) {
|
|
685
|
+
const modelReceipt = createModelReceiptTracker(ctx.model, ctx.modelCatalog?.models);
|
|
671
686
|
const { runId, agentId, child, lifecycle, journal, classifyFailure } = ctx;
|
|
672
687
|
let cancelRequested = false;
|
|
673
688
|
let emitQueue = Promise.resolve();
|
|
@@ -687,6 +702,8 @@ function wirePiRpcLifecycle(ctx) {
|
|
|
687
702
|
}
|
|
688
703
|
});
|
|
689
704
|
}
|
|
705
|
+
if (ctx.model)
|
|
706
|
+
enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: { type: 'status', label: 'starting_model', model: ctx.model } } }));
|
|
690
707
|
child.stdout?.on('data', (chunk) => {
|
|
691
708
|
const text = chunk.toString('utf8');
|
|
692
709
|
if (journal)
|
|
@@ -729,6 +746,9 @@ function wirePiRpcLifecycle(ctx) {
|
|
|
729
746
|
session = ctx.attachPiRpcSession({ child: ctx.child, prompt: ctx.prompt, send({ payload }) {
|
|
730
747
|
const translation = translateAgentRuntimeEvent({ rawEvent: payload });
|
|
731
748
|
if (translation.kind === 'agent') {
|
|
749
|
+
const receipt = modelReceipt(translation.payload);
|
|
750
|
+
if (receipt)
|
|
751
|
+
enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: receipt } }));
|
|
732
752
|
if (translation.sessionId !== undefined)
|
|
733
753
|
capturedSessionId = translation.sessionId;
|
|
734
754
|
if (translation.payload.type === 'tool_use') {
|
|
@@ -769,7 +789,7 @@ export function writePromptToStdin(def, child, prompt, handle) {
|
|
|
769
789
|
if (!stdin)
|
|
770
790
|
return;
|
|
771
791
|
if (def.promptInputFormat === 'stream-json') {
|
|
772
|
-
const line = JSON.stringify({ type: 'user', message: { role: 'user', content:
|
|
792
|
+
const line = JSON.stringify({ type: 'user', message: { role: 'user', content: messageContentWithImages({ prompt, images: handle.imageContents ?? [] }, {}) } });
|
|
773
793
|
stdin.write(`${line}\n`, 'utf8');
|
|
774
794
|
handle.recordSentBytes(prompt);
|
|
775
795
|
return;
|
|
@@ -789,6 +809,7 @@ export async function runAcpDispatch({ input, deps }) {
|
|
|
789
809
|
prompt: input.prompt,
|
|
790
810
|
cwd: input.cwd,
|
|
791
811
|
model: input.model,
|
|
812
|
+
...(input.modelCatalog ? { modelCatalog: input.modelCatalog } : {}),
|
|
792
813
|
imagePaths: input.imagePaths,
|
|
793
814
|
envFormat: input.envFormat,
|
|
794
815
|
// Mechanism 2 of 5 — see `WireAcpLifecycleContext.mcpServers`. `undefined` for any def that
|
|
@@ -825,6 +846,7 @@ export async function runPiRpcDispatch({ input, deps }) {
|
|
|
825
846
|
prompt: input.prompt,
|
|
826
847
|
cwd: input.cwd,
|
|
827
848
|
model: input.model,
|
|
849
|
+
...(input.modelCatalog ? { modelCatalog: input.modelCatalog } : {}),
|
|
828
850
|
imagePaths: input.imagePaths,
|
|
829
851
|
uploadRoot: input.uploadRoot,
|
|
830
852
|
attachPiRpcSession: deps.attachPiRpcSession,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { RuntimeAgentDef, RuntimeLockHold } from '@jini-ai/agent-runtime';
|
|
2
2
|
/**
|
|
3
3
|
* Phase 7: acquires a `runtimeLock` def's process-global mutex before `buildArgs` runs — see
|
|
4
4
|
* `RuntimeLock`'s own doc for the concrete race. A no-op (`undefined`) for the 23 of 24 defs with no
|
|
@@ -370,6 +370,6 @@ export declare function confirmChildSpawned({ input, deps }: {
|
|
|
370
370
|
* @complexity `run()`'s own setup is O(1); steady-state cost is the chosen stream parser's.
|
|
371
371
|
* @overallScore 100/100
|
|
372
372
|
*/
|
|
373
|
-
export declare function createAgentExecutor(requiredArgs: Pick<CreateAgentExecutorOptions, "lifecycle">, optionalArgs?: Pick<CreateAgentExecutorOptions, "getAgentDef" | "resolveAgentLaunch" | "ensureAgentCapabilities" | "applyAgentLaunchEnv" | "createCommandInvocation" | "spawn" | "attachAcpSession" | "acpPermissionHandler" | "attachPiRpcSession" | "preparePromptFileForAgent" | "prepareAgentLogFile" | "listProcessSnapshots" | "collectProcessTreePids" | "stopProcesses" | "onCleanupFailure" | "journal" | "continuation" | "classifyFailure" | "mcpJsonInjection" | "claudeConfigDirIsolation" | "claudeConfigDirIsolationEnabled" | "bufferedStdoutMaxBytes" | "promptAugmenter">): AgentExecutor;
|
|
373
|
+
export declare function createAgentExecutor(requiredArgs: Pick<CreateAgentExecutorOptions, "lifecycle">, optionalArgs?: Pick<CreateAgentExecutorOptions, "getAgentDef" | "resolveAgentLaunch" | "resolveModelForLaunch" | "ensureAgentCapabilities" | "applyAgentLaunchEnv" | "createCommandInvocation" | "spawn" | "attachAcpSession" | "acpPermissionHandler" | "attachPiRpcSession" | "preparePromptFileForAgent" | "prepareAgentLogFile" | "listProcessSnapshots" | "collectProcessTreePids" | "stopProcesses" | "onCleanupFailure" | "journal" | "continuation" | "classifyFailure" | "mcpJsonInjection" | "claudeConfigDirIsolation" | "claudeConfigDirIsolationEnabled" | "bufferedStdoutMaxBytes" | "promptAugmenter">): AgentExecutor;
|
|
374
374
|
export {};
|
|
375
375
|
//# sourceMappingURL=launch.d.ts.map
|
|
@@ -2,7 +2,7 @@ import { spawn as nodeSpawn, } from 'node:child_process';
|
|
|
2
2
|
import { promises as fsPromises, } from 'node:fs';
|
|
3
3
|
import { tmpdir, } from 'node:os';
|
|
4
4
|
import { join, } from 'node:path';
|
|
5
|
-
import { agentCapabilities, applyAgentLaunchEnv, ensureAgentCapabilities, getAgentDef, resolveAgentLaunch, attachAcpSession, attachPiRpcSession, checkPromptArgvBudget, checkWindowsCmdShimCommandLineBudget, checkWindowsDirectExeCommandLineBudget, prepareAgentLogFile, preparePromptFileForAgent, } from '@jini-ai/agent-runtime';
|
|
5
|
+
import { agentCapabilities, applyAgentLaunchEnv, ensureAgentCapabilities, resolveModelForLaunch, getAgentDef, resolveAgentLaunch, attachAcpSession, attachPiRpcSession, checkPromptArgvBudget, checkWindowsCmdShimCommandLineBudget, checkWindowsDirectExeCommandLineBudget, prepareAgentLogFile, preparePromptFileForAgent, } from '@jini-ai/agent-runtime';
|
|
6
6
|
import { collectProcessTreePids, createCommandInvocation, listProcessSnapshots, stopProcesses, } from '@jini-ai/platform';
|
|
7
7
|
import { applyImagePromptDelivery, } from '../image-prompt-delivery.js';
|
|
8
8
|
import { SUPPORTED_STREAM_FORMATS, AgentExecutorError, } from './contracts.js';
|
|
@@ -706,6 +706,23 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
706
706
|
const resolvedEnv = { ...(def.env ?? {}), ...resolveRunEnv({ input: input, hostEnv: process.env }) };
|
|
707
707
|
const launch = await resolveLaunch({ input: { runId: input.runId, def, resolvedEnv }, deps: { resolveAgentLaunch: resolveAgentLaunchFn, failBeforeSpawn } });
|
|
708
708
|
const spawnEnv = applyAgentLaunchEnvFn({ env: { ...resolvedEnv }, launch });
|
|
709
|
+
// Resolve from the launch environment before staging or sending any prompt. Pin the same
|
|
710
|
+
// concrete ID that the picker displays; an opaque native route cannot silently start a run.
|
|
711
|
+
let modelCatalog;
|
|
712
|
+
try {
|
|
713
|
+
const selection = await (options.resolveModelForLaunch ?? resolveModelForLaunch)({ def, context: {
|
|
714
|
+
executable: launch.launchPath, cwd: input.cwd, env: spawnEnv,
|
|
715
|
+
...(input.model && input.model !== 'default' ? { model: input.model } : {}),
|
|
716
|
+
...(input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : {}),
|
|
717
|
+
...(input.settingSources ? { settingSources: input.settingSources } : {}),
|
|
718
|
+
...(input.settings ? { settings: input.settings } : {}),
|
|
719
|
+
} });
|
|
720
|
+
input.model = selection.model;
|
|
721
|
+
modelCatalog = selection.catalog;
|
|
722
|
+
}
|
|
723
|
+
catch {
|
|
724
|
+
return failBeforeSpawn({ runId: input.runId, code: 'AGENT_MODEL_UNRESOLVED', message: 'AgentExecutor: pick a concrete model; the starting model could not be resolved for this launch.' });
|
|
725
|
+
}
|
|
709
726
|
// Fill the def's `--help` capability gate before `buildArgs` reads it. Without this, only a
|
|
710
727
|
// host that happened to call `detectAgents` in THIS process ever had the gate filled, so e.g.
|
|
711
728
|
// `claude` never got `--include-partial-messages` (no streamed text). Probes once per binary;
|
|
@@ -898,6 +915,8 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
898
915
|
const formatForStdin = { streamFormat };
|
|
899
916
|
const stdinHandle = isStdinDrivenFormat(formatForStdin)
|
|
900
917
|
? wireChildLifecycle({
|
|
918
|
+
startingModel: input.model,
|
|
919
|
+
modelCatalog,
|
|
901
920
|
runId: input.runId,
|
|
902
921
|
def,
|
|
903
922
|
streamFormat: formatForStdin.streamFormat,
|
|
@@ -937,6 +956,7 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
937
956
|
prompt: `${overlayDelivery.promptPrefix}${input.prompt}`,
|
|
938
957
|
cwd: input.cwd,
|
|
939
958
|
model: input.model,
|
|
959
|
+
modelCatalog,
|
|
940
960
|
imagePaths: input.imagePaths ?? [],
|
|
941
961
|
envFormat: def.acpMcpEnvFormat,
|
|
942
962
|
mcpBridge,
|
|
@@ -967,6 +987,7 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
967
987
|
prompt: `${overlayDelivery.promptPrefix}${input.prompt}`,
|
|
968
988
|
cwd: input.cwd,
|
|
969
989
|
model: input.model,
|
|
990
|
+
modelCatalog,
|
|
970
991
|
imagePaths: input.imagePaths ?? [],
|
|
971
992
|
uploadRoot: input.uploadRoot,
|
|
972
993
|
}, deps: {
|
|
@@ -994,6 +1015,8 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
994
1015
|
// fallback strategy, so their prefix is genuinely non-empty; what makes stdin the wrong
|
|
995
1016
|
// channel for them is the def's declared transport, not the strategy.
|
|
996
1017
|
const stdinPrompt = def.promptViaStdin === true ? overlayDelivery.prompt : imageDelivery.prompt;
|
|
1018
|
+
if (input.imageContents !== undefined)
|
|
1019
|
+
stdinHandle.imageContents = input.imageContents;
|
|
997
1020
|
writePromptToStdin(def, child, stdinPrompt, stdinHandle);
|
|
998
1021
|
}
|
|
999
1022
|
return { run };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { AcpMcpServerInput, RuntimeAgentDef } from '@jini-ai/agent-runtime';
|
|
2
|
+
import type { McpJsonInjectionOptions, McpJsonServerEntry, McpBridgeDelivery, PreparedCodexHome, PreparedClaudeConfigDir, ClaudeConfigDirIsolationOptions, FailBeforeSpawn } from './contracts.js';
|
|
3
3
|
/** The run-error code a run gets when its CLI reports the injected `jini` bridge as anything but connected. */
|
|
4
4
|
export declare const MCP_BRIDGE_UNAVAILABLE = "MCP_BRIDGE_UNAVAILABLE";
|
|
5
5
|
/**
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { promises as fsPromises, } from 'node:fs';
|
|
2
2
|
import { homedir, tmpdir, } from 'node:os';
|
|
3
3
|
import { join, } from 'node:path';
|
|
4
|
-
import {} from '@jini-ai/agent-runtime';
|
|
5
4
|
import { isRecord, errorMessage, } from './values.js';
|
|
6
|
-
import {} from './contracts.js';
|
|
7
5
|
const JINI_MCP_SERVER_KEY = 'jini';
|
|
8
6
|
/** The run-error code a run gets when its CLI reports the injected `jini` bridge as anything but connected. */
|
|
9
7
|
export const MCP_BRIDGE_UNAVAILABLE = 'MCP_BRIDGE_UNAVAILABLE';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RunAgentPayload } from '@jini-ai/protocol';
|
|
2
|
+
import { type DiscoveredModel } from '@jini-ai/agent-runtime';
|
|
3
|
+
/** Status metadata comes from CLI/RPC frames, never generated text. Dedupe repeats and record
|
|
4
|
+
* each observed transition separately so durable run events can describe model switches. */
|
|
5
|
+
export declare function createModelReceiptTracker(startingModel?: string, models?: readonly DiscoveredModel[]): (payload: RunAgentPayload) => RunAgentPayload | null;
|
|
6
|
+
//# sourceMappingURL=model-receipts.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { modelIdentityKind } from '@jini-ai/agent-runtime';
|
|
2
|
+
/** Status metadata comes from CLI/RPC frames, never generated text. Dedupe repeats and record
|
|
3
|
+
* each observed transition separately so durable run events can describe model switches. */
|
|
4
|
+
export function createModelReceiptTracker(startingModel, models = []) {
|
|
5
|
+
// The catalog is launch-scoped evidence. Normalize both the pinned and reported names
|
|
6
|
+
// so a CLI's alias/display spelling does not fabricate a durable model transition.
|
|
7
|
+
const normalize = (id) => {
|
|
8
|
+
const row = models.find(model => model.id === id) || models.find(model => model.label === id);
|
|
9
|
+
return row?.resolvedId || row?.id || id;
|
|
10
|
+
};
|
|
11
|
+
let current = startingModel && normalize(startingModel);
|
|
12
|
+
return (payload) => {
|
|
13
|
+
if (payload.type !== 'status' || !payload.model || typeof payload.model !== 'string')
|
|
14
|
+
return null;
|
|
15
|
+
const observed = normalize(payload.model);
|
|
16
|
+
if (modelIdentityKind(observed) !== 'concrete')
|
|
17
|
+
return null;
|
|
18
|
+
if (observed === current)
|
|
19
|
+
return null;
|
|
20
|
+
const prior = current;
|
|
21
|
+
current = observed;
|
|
22
|
+
return { type: 'status', label: prior ? 'model_switch' : 'observed_model', model: current,
|
|
23
|
+
...(prior ? { previousModel: prior, detail: `Previous model: ${prior}` } : {}) };
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=model-receipts.js.map
|