@jini-ai/daemon 0.5.3 → 0.6.0
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 +4 -0
- package/dist/agent-executor/contracts.d.ts +45 -14
- 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 +0 -2
- package/dist/agent-executor/events.d.ts +26 -6
- package/dist/agent-executor/events.js +198 -11
- package/dist/agent-executor/handoff.d.ts +1 -1
- package/dist/agent-executor/handoff.js +0 -1
- package/dist/agent-executor/index.d.ts +1 -1
- package/dist/agent-executor/launch.d.ts +1 -1
- package/dist/agent-executor/launch.js +47 -3
- package/dist/agent-executor/mcp-bridge.d.ts +2 -2
- package/dist/agent-executor/mcp-bridge.js +19 -8
- 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 +7 -1665
- package/dist/agent-executor.js +7 -3007
- 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/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/index.d.ts +2 -2
- package/dist/http/index.js +1 -1
- 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/route-manifest.js +2 -2
- 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 +31 -6
- package/dist/http/runs.js +36 -1
- 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 +75 -571
- package/dist/index.js +75 -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/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
|
@@ -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';
|
|
@@ -640,6 +640,18 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
640
640
|
await lifecycle.finish({ runId, status: 'failed', code: null, signal: null, resumable: false });
|
|
641
641
|
throw new AgentExecutorError({ code: code, message: message });
|
|
642
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* Every run with a live child, keyed by run id, mapped to how a mid-run message reaches it — the
|
|
645
|
+
* stdin handle's `sendUserMessage` for a stdin-driven run, a constant `'unsupported'` for an
|
|
646
|
+
* ACP/pi run (whose RPC takes one prompt per turn). Removed on the child's `'close'`, so a
|
|
647
|
+
* finished or unknown run reads as `'not-running'`. Bounded by the number of live children.
|
|
648
|
+
*/
|
|
649
|
+
const liveRunInputs = new Map();
|
|
650
|
+
/** `AgentExecutor.sendUserMessage()` — see that interface method's own doc. @complexity O(1). */
|
|
651
|
+
async function sendUserMessage({ runId, text }) {
|
|
652
|
+
const deliver = liveRunInputs.get(runId);
|
|
653
|
+
return deliver ? deliver(text) : 'not-running';
|
|
654
|
+
}
|
|
643
655
|
/**
|
|
644
656
|
* `AgentExecutor.run()` — see that interface method's own doc for the
|
|
645
657
|
* public contract. Implementation note on shape: every guard below
|
|
@@ -706,6 +718,23 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
706
718
|
const resolvedEnv = { ...(def.env ?? {}), ...resolveRunEnv({ input: input, hostEnv: process.env }) };
|
|
707
719
|
const launch = await resolveLaunch({ input: { runId: input.runId, def, resolvedEnv }, deps: { resolveAgentLaunch: resolveAgentLaunchFn, failBeforeSpawn } });
|
|
708
720
|
const spawnEnv = applyAgentLaunchEnvFn({ env: { ...resolvedEnv }, launch });
|
|
721
|
+
// Resolve from the launch environment before staging or sending any prompt. Pin the same
|
|
722
|
+
// concrete ID that the picker displays; an opaque native route cannot silently start a run.
|
|
723
|
+
let modelCatalog;
|
|
724
|
+
try {
|
|
725
|
+
const selection = await (options.resolveModelForLaunch ?? resolveModelForLaunch)({ def, context: {
|
|
726
|
+
executable: launch.launchPath, cwd: input.cwd, env: spawnEnv,
|
|
727
|
+
...(input.model && input.model !== 'default' ? { model: input.model } : {}),
|
|
728
|
+
...(input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : {}),
|
|
729
|
+
...(input.settingSources ? { settingSources: input.settingSources } : {}),
|
|
730
|
+
...(input.settings ? { settings: input.settings } : {}),
|
|
731
|
+
} });
|
|
732
|
+
input.model = selection.model;
|
|
733
|
+
modelCatalog = selection.catalog;
|
|
734
|
+
}
|
|
735
|
+
catch {
|
|
736
|
+
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.' });
|
|
737
|
+
}
|
|
709
738
|
// Fill the def's `--help` capability gate before `buildArgs` reads it. Without this, only a
|
|
710
739
|
// host that happened to call `detectAgents` in THIS process ever had the gate filled, so e.g.
|
|
711
740
|
// `claude` never got `--include-partial-messages` (no streamed text). Probes once per binary;
|
|
@@ -898,6 +927,8 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
898
927
|
const formatForStdin = { streamFormat };
|
|
899
928
|
const stdinHandle = isStdinDrivenFormat(formatForStdin)
|
|
900
929
|
? wireChildLifecycle({
|
|
930
|
+
startingModel: input.model,
|
|
931
|
+
modelCatalog,
|
|
901
932
|
runId: input.runId,
|
|
902
933
|
def,
|
|
903
934
|
streamFormat: formatForStdin.streamFormat,
|
|
@@ -915,7 +946,18 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
915
946
|
expectsJiniBridge: writtenMcpJsonPath !== undefined,
|
|
916
947
|
})
|
|
917
948
|
: null;
|
|
918
|
-
|
|
949
|
+
// Registered with the lifecycle wiring above, before the spawn-confirmation await, so a child
|
|
950
|
+
// that closes immediately never leaves a stale entry behind.
|
|
951
|
+
const runId = input.runId;
|
|
952
|
+
liveRunInputs.set(runId, stdinHandle ? stdinHandle.sendUserMessage : async () => 'unsupported');
|
|
953
|
+
child.once('close', () => liveRunInputs.delete(runId));
|
|
954
|
+
try {
|
|
955
|
+
await confirmChildSpawned({ input: { runId: input.runId, def, child }, deps: { releaseStagedResources, failBeforeSpawn } });
|
|
956
|
+
}
|
|
957
|
+
catch (error) {
|
|
958
|
+
liveRunInputs.delete(runId);
|
|
959
|
+
throw error;
|
|
960
|
+
}
|
|
919
961
|
// Now — and only now — is there a live process that could consume the locked side effect, so
|
|
920
962
|
// this is where a def's handoff watcher starts. Deliberately not awaited: the whole point is to
|
|
921
963
|
// release the lock as soon as the child confirms the handoff, in parallel with this run
|
|
@@ -937,6 +979,7 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
937
979
|
prompt: `${overlayDelivery.promptPrefix}${input.prompt}`,
|
|
938
980
|
cwd: input.cwd,
|
|
939
981
|
model: input.model,
|
|
982
|
+
modelCatalog,
|
|
940
983
|
imagePaths: input.imagePaths ?? [],
|
|
941
984
|
envFormat: def.acpMcpEnvFormat,
|
|
942
985
|
mcpBridge,
|
|
@@ -967,6 +1010,7 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
967
1010
|
prompt: `${overlayDelivery.promptPrefix}${input.prompt}`,
|
|
968
1011
|
cwd: input.cwd,
|
|
969
1012
|
model: input.model,
|
|
1013
|
+
modelCatalog,
|
|
970
1014
|
imagePaths: input.imagePaths ?? [],
|
|
971
1015
|
uploadRoot: input.uploadRoot,
|
|
972
1016
|
}, deps: {
|
|
@@ -998,6 +1042,6 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
|
|
|
998
1042
|
stdinHandle.imageContents = input.imageContents;
|
|
999
1043
|
writePromptToStdin(def, child, stdinPrompt, stdinHandle);
|
|
1000
1044
|
}
|
|
1001
|
-
return { run };
|
|
1045
|
+
return { run, sendUserMessage };
|
|
1002
1046
|
}
|
|
1003
1047
|
//# sourceMappingURL=launch.js.map
|
|
@@ -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
|
-
import { join, } from 'node:path';
|
|
4
|
-
import {} from '@jini-ai/agent-runtime';
|
|
3
|
+
import { join, resolve, } from 'node:path';
|
|
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';
|
|
@@ -554,12 +552,23 @@ function defaultMkdtempCodexHome({ prefix }) {
|
|
|
554
552
|
function defaultRemoveCodexHomeDir({ path }) {
|
|
555
553
|
return fsPromises.rm(path, { recursive: true, force: true });
|
|
556
554
|
}
|
|
555
|
+
/** Config credentials stay run-scoped, but rollouts must survive both completion and cancellation.
|
|
556
|
+
* Native directory links keep Codex as the sole writer of its own persisted session format.
|
|
557
|
+
* Cleanup removes the links themselves, never their targets. Two fixed directories, O(1) I/O. */
|
|
558
|
+
async function defaultLinkCodexSessionStore({ runHome, sourceHome }, _optional = {}) {
|
|
559
|
+
for (const name of ['sessions', 'archived_sessions']) {
|
|
560
|
+
const target = resolve(sourceHome, name);
|
|
561
|
+
await fsPromises.mkdir(target, { recursive: true, mode: 0o700 });
|
|
562
|
+
await fsPromises.symlink(target, join(runHome, name), 'junction');
|
|
563
|
+
}
|
|
564
|
+
}
|
|
557
565
|
function resolveCodexHomeSeams(options) {
|
|
558
566
|
return {
|
|
559
567
|
mkdtemp: options.mkdtemp ?? defaultMkdtempCodexHome,
|
|
560
568
|
readFile: options.readFile ?? defaultReadMcpJsonFile,
|
|
561
569
|
writeFile: options.writeFile ?? defaultWriteMcpJsonFile,
|
|
562
570
|
removeDir: options.removeDir ?? defaultRemoveCodexHomeDir,
|
|
571
|
+
linkSessionStore: options.linkSessionStore ?? defaultLinkCodexSessionStore,
|
|
563
572
|
};
|
|
564
573
|
}
|
|
565
574
|
/**
|
|
@@ -575,8 +584,9 @@ function resolveCodexHomeSeams(options) {
|
|
|
575
584
|
* 0.151.0) to fail fast with a structured `401 Unauthorized` stream event, never an interactive
|
|
576
585
|
* login prompt or a hang — see `defs/codex.ts`'s module doc for the full transcript summary.
|
|
577
586
|
*
|
|
578
|
-
* **Never
|
|
579
|
-
*
|
|
587
|
+
* **Never changes the real config or credentials.** Those source files stay read-only. Only the
|
|
588
|
+
* native rollout directories are shared: deleting a run's scratch home must not erase the thread
|
|
589
|
+
* whose id the host already persisted. No copied login or run-bound MCP credential is retained.
|
|
580
590
|
*
|
|
581
591
|
* A failure after the directory is created (a rejecting `writeFile`, most plausibly) does not leak
|
|
582
592
|
* it: the directory may already hold a partial `config.toml` or a copied credential, so the
|
|
@@ -586,9 +596,9 @@ function resolveCodexHomeSeams(options) {
|
|
|
586
596
|
* `@jini-ai/agent-runtime`'s `prepareAgentLogFile`'s `label` is.
|
|
587
597
|
* @param entry - The shared bridge entry.
|
|
588
598
|
* @param sourceCodexHomeDir - Where to read the real install's `config.toml`/`auth.json` from — see {@link resolveSourceCodexHomeDir}.
|
|
589
|
-
* @param seams - Injectable mkdtemp/readFile/writeFile/removeDir, real filesystem by default.
|
|
590
|
-
* @throws Whatever
|
|
591
|
-
* @complexity O(1)
|
|
599
|
+
* @param seams - Injectable mkdtemp/readFile/writeFile/removeDir/linkSessionStore, real filesystem by default.
|
|
600
|
+
* @throws Whatever staging rejects with — the caller ({@link prepareCodexHomeIfNeeded}) turns that into a pre-spawn `AGENT_SPAWN_FAILED` failure, matching {@link writeMcpJsonForRun}'s own contract.
|
|
601
|
+
* @complexity O(1): fixed directory links and up to two best-effort file read/write round trips.
|
|
592
602
|
* @overallScore 100/100
|
|
593
603
|
*/
|
|
594
604
|
async function prepareCodexHomeForRun(runId, entry, sourceCodexHomeDir, seams) {
|
|
@@ -618,6 +628,7 @@ async function prepareCodexHomeForRun(runId, entry, sourceCodexHomeDir, seams) {
|
|
|
618
628
|
// No stored login (or unreadable) — the spawned CLI runs unauthenticated. Confirmed above:
|
|
619
629
|
// this fails the run fast and observably, never as a hang.
|
|
620
630
|
}
|
|
631
|
+
await seams.linkSessionStore({ runHome: dir, sourceHome: sourceCodexHomeDir }, {});
|
|
621
632
|
}
|
|
622
633
|
catch (err) {
|
|
623
634
|
await seams.removeDir({ path: dir }).catch(() => {
|
|
@@ -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
|