@agentproto/driver-agent-cli
Version:
@agentproto/driver-agent-cli — AIP-45 AGENT-CLI.md reference implementation. Multi-protocol runner that loads an AGENT-CLI.md manifest, spawns the binary, and dispatches turns through an ACP / MCP / proprietary arm. ACP arm delegates to @agentproto/acp; e
296 lines (284 loc) • 9.61 kB
JavaScript
export { LEGACY_GATEWAY_MODE_IDS, LEGACY_POSTURE_MODE_IDS, RuntimeConfigError, agentCliFrontmatterSchema, agentCliSupportsHostContextIsolation, applyModelCommand, autoAllowPermissionHandler, composeSpawn, createAcpProtocolArm, createAgentCliRuntime, createArmSessionControls, createJcodeMapperState, createMastraMapperState, createPrintSession, createProprietaryProtocolArm, defineAgentCli, hostContextExcludes, inferLegacyModeKind, isLegacyExtractedModeId, isModelSwitchAcknowledgement, mapJcodeEvent, mapMastraEvent, parseModelSwitchCommand, planModePermissionHandler, promptTurn, resolveContinuationStrategy, runtimeConfigSchema, toFileBasedMcpServers } from './chunk-Y3FGLQ4R.mjs';
/**
* @agentproto/driver-agent-cli v0.1.0-alpha
* AIP-45 AGENT-CLI.md `defineAgentCli` reference implementation.
*/
// src/model.ts
function datasetPreamble(datasetDir) {
return `You have direct read access to the source files under \`${datasetDir}\` (e.g. \`sources/\` raw captures, \`entries/\` refined notes). Use your file-reading tools to consult the primary sources directly when grounding claims \u2014 do not rely only on the excerpts quoted in this prompt.`;
}
function composePrompt(system, prompt, datasetDir) {
const head = datasetDir ? `${datasetPreamble(datasetDir)}
` : "";
return system ? `${head}${system}
${prompt}` : `${head}${prompt}`;
}
var PROVIDER_KEY_ENV = [
"OPENROUTER_API_KEY",
"ANTHROPIC_API_KEY",
"OPENAI_API_KEY"
];
function resolveCliEnv(opts) {
if (opts.env) return opts.env;
const env = {};
for (const k of PROVIDER_KEY_ENV) {
const v = process.env[k];
if (v) env[k] = v;
}
return env;
}
async function driveTurn(runtime, prompt, opts) {
const controller = new AbortController();
const timer = setTimeout(
() => controller.abort(),
opts.timeoutMs ?? 5 * 60 * 1e3
);
const cwd = opts.cwd ?? opts.datasetDir;
const session = await runtime.start({
env: resolveCliEnv(opts),
signal: controller.signal,
...cwd ? { cwd } : {}
});
try {
let text = "";
for await (const evt of session.send({ role: "user", content: prompt })) {
if (evt.kind === "text-delta") {
text += evt.text;
} else if (evt.kind === "error") {
throw new Error(`${runtime.definition.id} model: ${evt.error.message}`);
} else if (evt.kind === "turn-end") {
if (evt.reason !== "completed")
throw new Error(`${runtime.definition.id} model: turn ${evt.reason}`);
break;
}
}
if (controller.signal.aborted)
throw new Error(`${runtime.definition.id} model: timed out`);
return text.trim();
} finally {
clearTimeout(timer);
await session.close();
}
}
function makeAgentCliModel(runtime, opts = {}) {
const run = opts.run ?? ((prompt) => driveTurn(runtime, prompt, opts));
return {
async complete({ system, prompt }) {
return {
result: await run(composePrompt(system, prompt, opts.datasetDir))
};
}
};
}
// src/continuation/strategies/none.ts
var noneStrategy = {
id: "none",
async acquire(ctx) {
return ctx.runtime.start(ctx.startOptions);
},
async release(ctx) {
await ctx.session.close();
}
};
// src/continuation/types.ts
function deriveKeyFromScope(scope, turnCtx) {
const parts = [];
for (const s of scope) {
const v = turnCtx[s];
if (v) parts.push(`${s}=${v}`);
}
return parts.length === 0 ? null : parts.join("|");
}
// src/continuation/strategies/pinned-session.ts
var DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
var pins = /* @__PURE__ */ new Map();
function clearIdleTimer(entry) {
if (entry.idleTimer) {
clearTimeout(entry.idleTimer);
entry.idleTimer = null;
}
}
function scheduleIdleEviction(key, entry, idleTimeoutMs) {
clearIdleTimer(entry);
entry.idleTimer = setTimeout(() => {
pins.delete(key);
entry.session.close().catch((err) => {
console.warn(
`[pinned-session] idle close failed for ${key}:`,
err instanceof Error ? err.message : err
);
});
console.log(
`[pinned-session] ${key} idle ${idleTimeoutMs / 6e4}m \u2192 closed`
);
}, idleTimeoutMs);
entry.idleTimer.unref?.();
}
function evictPin(key) {
const entry = pins.get(key);
if (!entry) return;
clearIdleTimer(entry);
pins.delete(key);
entry.session.close().catch((err) => {
console.warn(
`[pinned-session] evict close failed for ${key}:`,
err instanceof Error ? err.message : err
);
});
}
var pinnedSessionStrategy = {
id: "pinned-session",
async acquire(ctx) {
const tuning = ctx.runtime.definition.continuation?.pinned_session;
const scope = tuning?.key_scope ?? ["conversation", "operator"];
const key = deriveKeyFromScope(scope, ctx.turnCtx);
const idleTimeoutMs = tuning?.idle_timeout_ms ?? DEFAULT_IDLE_TIMEOUT_MS;
if (key === null) {
console.warn(
`[pinned-session] turnCtx has no fields matching key_scope ${JSON.stringify(scope)}; falling back to per-spawn (no pin).`
);
return ctx.runtime.start(ctx.startOptions);
}
const existing = pins.get(key);
if (existing) {
clearIdleTimer(existing);
return existing.session;
}
const session = await ctx.runtime.start(ctx.startOptions);
const entry = {
session,
runtime: ctx.runtime,
startOptions: ctx.startOptions,
idleTimer: null
};
pins.set(key, entry);
scheduleIdleEviction(key, entry, idleTimeoutMs);
return session;
},
async release(ctx) {
let foundKey = null;
for (const [k, e] of pins) {
if (e.session === ctx.session) {
foundKey = k;
break;
}
}
if (!foundKey) {
await ctx.session.close();
return;
}
const entry = pins.get(foundKey);
const tuning = entry.runtime.definition.continuation?.pinned_session;
const idleTimeoutMs = tuning?.idle_timeout_ms ?? DEFAULT_IDLE_TIMEOUT_MS;
if (ctx.outcome.kind === "error" && ctx.outcome.reason === "fatal") {
evictPin(foundKey);
return;
}
scheduleIdleEviction(foundKey, entry, idleTimeoutMs);
}
};
// src/continuation/strategies/transcript.ts
var transcriptStrategy = {
id: "transcript",
async acquire(ctx) {
return ctx.runtime.start(ctx.startOptions);
},
async release(ctx) {
await ctx.session.close();
}
};
// src/continuation/strategies/native-resume.ts
var _hooks = null;
function configureNativeResume(hooks) {
_hooks = hooks;
}
var nativeResumeStrategy = {
id: "native-resume",
async acquire(ctx) {
if (!_hooks) {
console.warn(
"[native-resume] no hooks registered (call configureNativeResume at boot); falling back to per-spawn \u2014 no continuity."
);
return ctx.runtime.start(ctx.startOptions);
}
const scope = ctx.runtime.definition.continuation?.pinned_session?.key_scope ?? [
"conversation",
"operator"
];
const key = deriveKeyFromScope(scope, ctx.turnCtx);
if (key === null) {
console.warn(
`[native-resume] turnCtx has no fields matching key_scope ${JSON.stringify(scope)}; falling back to per-spawn (no resume).`
);
return ctx.runtime.start(ctx.startOptions);
}
const existingId = await _hooks.load(ctx.turnCtx).catch((err) => {
console.warn(
`[native-resume] load hook failed for ${key}:`,
err instanceof Error ? err.message : err
);
return void 0;
});
let session;
let resumed = false;
if (existingId) {
try {
session = await ctx.runtime.start({
...ctx.startOptions,
resumeSessionId: existingId
});
resumed = true;
} catch (err) {
console.warn(
`[native-resume] resume failed for ${key} (id=${existingId.slice(0, 12)}\u2026), starting fresh:`,
err instanceof Error ? err.message : err
);
if (_hooks.forget) {
await _hooks.forget(ctx.turnCtx).catch(() => void 0);
}
session = await ctx.runtime.start(ctx.startOptions);
}
} else {
session = await ctx.runtime.start(ctx.startOptions);
}
if (!resumed && session.sessionId) {
await _hooks.save(ctx.turnCtx, session.sessionId).catch((err) => {
console.warn(
`[native-resume] save hook failed for ${key}:`,
err instanceof Error ? err.message : err
);
});
}
return session;
},
async release(ctx) {
await ctx.session.close();
}
};
// src/continuation/registry.ts
var registry = /* @__PURE__ */ new Map();
function registerContinuationStrategy(strategy) {
registry.set(strategy.id, strategy);
}
function getContinuationStrategy(id) {
const s = registry.get(id);
if (!s) {
throw new Error(
`[agent-cli] No continuation strategy registered for id '${id}'. Built-ins: ${Array.from(registry.keys()).join(", ")}.`
);
}
return s;
}
function listContinuationStrategies() {
return Array.from(registry.keys());
}
registerContinuationStrategy(noneStrategy);
registerContinuationStrategy(pinnedSessionStrategy);
registerContinuationStrategy(transcriptStrategy);
registerContinuationStrategy(nativeResumeStrategy);
// src/index.ts
var SPEC_NAME = "agentcli-interactive/v1";
var SPEC_VERSION = "0.1.0-alpha";
export { PROVIDER_KEY_ENV, SPEC_NAME, SPEC_VERSION, composePrompt, configureNativeResume, datasetPreamble, deriveKeyFromScope, getContinuationStrategy, listContinuationStrategies, makeAgentCliModel, registerContinuationStrategy, resolveCliEnv };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map