@alpacakit/agents 0.1.0-beta.19
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/LICENSE +21 -0
- package/README.md +224 -0
- package/dist/agents/builtin.d.ts +19 -0
- package/dist/agents/builtin.d.ts.map +1 -0
- package/dist/agents/builtin.js +14 -0
- package/dist/agents/claude.d.ts +20 -0
- package/dist/agents/claude.d.ts.map +1 -0
- package/dist/agents/claude.js +480 -0
- package/dist/agents/codex.d.ts +19 -0
- package/dist/agents/codex.d.ts.map +1 -0
- package/dist/agents/codex.js +355 -0
- package/dist/agents/openai-compatible/config.d.ts +16 -0
- package/dist/agents/openai-compatible/config.d.ts.map +1 -0
- package/dist/agents/openai-compatible/config.js +160 -0
- package/dist/agents/openai-compatible/engine.d.ts +18 -0
- package/dist/agents/openai-compatible/engine.d.ts.map +1 -0
- package/dist/agents/openai-compatible/engine.js +10 -0
- package/dist/agents/openai-compatible/fetch-engine.d.ts +38 -0
- package/dist/agents/openai-compatible/fetch-engine.d.ts.map +1 -0
- package/dist/agents/openai-compatible/fetch-engine.js +216 -0
- package/dist/agents/openai-compatible/pi/engine.d.ts +32 -0
- package/dist/agents/openai-compatible/pi/engine.d.ts.map +1 -0
- package/dist/agents/openai-compatible/pi/engine.js +98 -0
- package/dist/agents/openai-compatible/pi/module.d.ts +87 -0
- package/dist/agents/openai-compatible/pi/module.d.ts.map +1 -0
- package/dist/agents/openai-compatible/pi/module.js +43 -0
- package/dist/agents/openai-compatible/pi/sandbox.d.ts +25 -0
- package/dist/agents/openai-compatible/pi/sandbox.d.ts.map +1 -0
- package/dist/agents/openai-compatible/pi/sandbox.js +69 -0
- package/dist/agents/openai-compatible/pi/session.d.ts +17 -0
- package/dist/agents/openai-compatible/pi/session.d.ts.map +1 -0
- package/dist/agents/openai-compatible/pi/session.js +354 -0
- package/dist/agents/openai-compatible/response.d.ts +19 -0
- package/dist/agents/openai-compatible/response.d.ts.map +1 -0
- package/dist/agents/openai-compatible/response.js +203 -0
- package/dist/agents/openai-compatible/types.d.ts +34 -0
- package/dist/agents/openai-compatible/types.d.ts.map +1 -0
- package/dist/agents/openai-compatible/types.js +1 -0
- package/dist/agents/openai-compatible.d.ts +12 -0
- package/dist/agents/openai-compatible.d.ts.map +1 -0
- package/dist/agents/openai-compatible.js +45 -0
- package/dist/auth/login-probe.d.ts +21 -0
- package/dist/auth/login-probe.d.ts.map +1 -0
- package/dist/auth/login-probe.js +47 -0
- package/dist/auth/login.d.ts +79 -0
- package/dist/auth/login.d.ts.map +1 -0
- package/dist/auth/login.js +9 -0
- package/dist/auth/material.d.ts +87 -0
- package/dist/auth/material.d.ts.map +1 -0
- package/dist/auth/material.js +59 -0
- package/dist/auth/profile-store.d.ts +14 -0
- package/dist/auth/profile-store.d.ts.map +1 -0
- package/dist/auth/profile-store.js +101 -0
- package/dist/auth/secrets.d.ts +14 -0
- package/dist/auth/secrets.d.ts.map +1 -0
- package/dist/auth/secrets.js +40 -0
- package/dist/codex/app-server.d.ts +30 -0
- package/dist/codex/app-server.d.ts.map +1 -0
- package/dist/codex/app-server.js +322 -0
- package/dist/codex/index.d.ts +3 -0
- package/dist/codex/index.d.ts.map +1 -0
- package/dist/codex/index.js +1 -0
- package/dist/codex/protocol.d.ts +124 -0
- package/dist/codex/protocol.d.ts.map +1 -0
- package/dist/codex/protocol.js +283 -0
- package/dist/config/agent-config-validation.d.ts +22 -0
- package/dist/config/agent-config-validation.d.ts.map +1 -0
- package/dist/config/agent-config-validation.js +31 -0
- package/dist/config/agent-configs.d.ts +37 -0
- package/dist/config/agent-configs.d.ts.map +1 -0
- package/dist/config/agent-configs.js +214 -0
- package/dist/config/compose.d.ts +10 -0
- package/dist/config/compose.d.ts.map +1 -0
- package/dist/config/compose.js +89 -0
- package/dist/config/config.d.ts +21 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +33 -0
- package/dist/config/errors.d.ts +45 -0
- package/dist/config/errors.d.ts.map +1 -0
- package/dist/config/errors.js +37 -0
- package/dist/config/extension.d.ts +3 -0
- package/dist/config/extension.d.ts.map +1 -0
- package/dist/config/extension.js +1 -0
- package/dist/config/ids.d.ts +8 -0
- package/dist/config/ids.d.ts.map +1 -0
- package/dist/config/ids.js +4 -0
- package/dist/config/index.d.ts +12 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +6 -0
- package/dist/config/model.d.ts +105 -0
- package/dist/config/model.d.ts.map +1 -0
- package/dist/config/model.js +1 -0
- package/dist/config/paths.d.ts +7 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +10 -0
- package/dist/config/schema.d.ts +59 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +83 -0
- package/dist/config/store.d.ts +34 -0
- package/dist/config/store.d.ts.map +1 -0
- package/dist/config/store.js +420 -0
- package/dist/config/support.d.ts +14 -0
- package/dist/config/support.d.ts.map +1 -0
- package/dist/config/support.js +32 -0
- package/dist/config/values.d.ts +21 -0
- package/dist/config/values.d.ts.map +1 -0
- package/dist/config/values.js +22 -0
- package/dist/config/xdg.d.ts +20 -0
- package/dist/config/xdg.d.ts.map +1 -0
- package/dist/config/xdg.js +23 -0
- package/dist/core/async-queue.d.ts +14 -0
- package/dist/core/async-queue.d.ts.map +1 -0
- package/dist/core/async-queue.js +46 -0
- package/dist/core/env.d.ts +19 -0
- package/dist/core/env.d.ts.map +1 -0
- package/dist/core/env.js +39 -0
- package/dist/core/error.d.ts +121 -0
- package/dist/core/error.d.ts.map +1 -0
- package/dist/core/error.js +88 -0
- package/dist/core/fs.d.ts +16 -0
- package/dist/core/fs.d.ts.map +1 -0
- package/dist/core/fs.js +30 -0
- package/dist/core/json.d.ts +20 -0
- package/dist/core/json.d.ts.map +1 -0
- package/dist/core/json.js +42 -0
- package/dist/core/node-ports.d.ts +9 -0
- package/dist/core/node-ports.d.ts.map +1 -0
- package/dist/core/node-ports.js +135 -0
- package/dist/core/ports.d.ts +71 -0
- package/dist/core/ports.d.ts.map +1 -0
- package/dist/core/ports.js +19 -0
- package/dist/core/redact.d.ts +8 -0
- package/dist/core/redact.d.ts.map +1 -0
- package/dist/core/redact.js +66 -0
- package/dist/core/run.d.ts +125 -0
- package/dist/core/run.d.ts.map +1 -0
- package/dist/core/run.js +27 -0
- package/dist/core/values.d.ts +12 -0
- package/dist/core/values.d.ts.map +1 -0
- package/dist/core/values.js +9 -0
- package/dist/corpus/failure-corpus.d.ts +21 -0
- package/dist/corpus/failure-corpus.d.ts.map +1 -0
- package/dist/corpus/failure-corpus.js +89 -0
- package/dist/fleet/fleet.d.ts +120 -0
- package/dist/fleet/fleet.d.ts.map +1 -0
- package/dist/fleet/fleet.js +409 -0
- package/dist/fleet/policy.d.ts +45 -0
- package/dist/fleet/policy.d.ts.map +1 -0
- package/dist/fleet/policy.js +50 -0
- package/dist/fleet/store.d.ts +28 -0
- package/dist/fleet/store.d.ts.map +1 -0
- package/dist/fleet/store.js +225 -0
- package/dist/host/capabilities.d.ts +146 -0
- package/dist/host/capabilities.d.ts.map +1 -0
- package/dist/host/capabilities.js +59 -0
- package/dist/host/context.d.ts +29 -0
- package/dist/host/context.d.ts.map +1 -0
- package/dist/host/context.js +11 -0
- package/dist/host/github-dir.d.ts +16 -0
- package/dist/host/github-dir.d.ts.map +1 -0
- package/dist/host/github-dir.js +164 -0
- package/dist/host/mcp-list.d.ts +12 -0
- package/dist/host/mcp-list.d.ts.map +1 -0
- package/dist/host/mcp-list.js +122 -0
- package/dist/host/requirement-ref.d.ts +12 -0
- package/dist/host/requirement-ref.d.ts.map +1 -0
- package/dist/host/requirement-ref.js +40 -0
- package/dist/host/run-command.d.ts +27 -0
- package/dist/host/run-command.d.ts.map +1 -0
- package/dist/host/run-command.js +66 -0
- package/dist/host/validate-spec.d.ts +10 -0
- package/dist/host/validate-spec.d.ts.map +1 -0
- package/dist/host/validate-spec.js +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/kernel/classify.d.ts +44 -0
- package/dist/kernel/classify.d.ts.map +1 -0
- package/dist/kernel/classify.js +136 -0
- package/dist/kernel/cli-agent.d.ts +62 -0
- package/dist/kernel/cli-agent.d.ts.map +1 -0
- package/dist/kernel/cli-agent.js +281 -0
- package/dist/kernel/cli-login.d.ts +13 -0
- package/dist/kernel/cli-login.d.ts.map +1 -0
- package/dist/kernel/cli-login.js +199 -0
- package/dist/kernel/definition.d.ts +41 -0
- package/dist/kernel/definition.d.ts.map +1 -0
- package/dist/kernel/definition.js +13 -0
- package/dist/kernel/env-template.d.ts +17 -0
- package/dist/kernel/env-template.d.ts.map +1 -0
- package/dist/kernel/env-template.js +78 -0
- package/dist/kernel/facets.d.ts +58 -0
- package/dist/kernel/facets.d.ts.map +1 -0
- package/dist/kernel/facets.js +102 -0
- package/dist/kernel/guard.d.ts +24 -0
- package/dist/kernel/guard.d.ts.map +1 -0
- package/dist/kernel/guard.js +69 -0
- package/dist/kernel/handle.d.ts +29 -0
- package/dist/kernel/handle.d.ts.map +1 -0
- package/dist/kernel/handle.js +97 -0
- package/dist/kernel/keyed.d.ts +19 -0
- package/dist/kernel/keyed.d.ts.map +1 -0
- package/dist/kernel/keyed.js +35 -0
- package/dist/kernel/login.d.ts +20 -0
- package/dist/kernel/login.d.ts.map +1 -0
- package/dist/kernel/login.js +74 -0
- package/dist/kernel/options.d.ts +73 -0
- package/dist/kernel/options.d.ts.map +1 -0
- package/dist/kernel/options.js +41 -0
- package/dist/kernel/run.d.ts +20 -0
- package/dist/kernel/run.d.ts.map +1 -0
- package/dist/kernel/run.js +245 -0
- package/dist/kernel/runtime.d.ts +48 -0
- package/dist/kernel/runtime.d.ts.map +1 -0
- package/dist/kernel/runtime.js +108 -0
- package/dist/keyring/index.d.ts +2 -0
- package/dist/keyring/index.d.ts.map +1 -0
- package/dist/keyring/index.js +1 -0
- package/dist/keyring/resolver.d.ts +9 -0
- package/dist/keyring/resolver.d.ts.map +1 -0
- package/dist/keyring/resolver.js +62 -0
- package/dist/run-agent.d.ts +19 -0
- package/dist/run-agent.d.ts.map +1 -0
- package/dist/run-agent.js +25 -0
- package/dist/structured/extract.d.ts +27 -0
- package/dist/structured/extract.d.ts.map +1 -0
- package/dist/structured/extract.js +188 -0
- package/dist/structured/run-structured.d.ts +83 -0
- package/dist/structured/run-structured.d.ts.map +1 -0
- package/dist/structured/run-structured.js +202 -0
- package/dist/structured/standard-schema.d.ts +34 -0
- package/dist/structured/standard-schema.d.ts.map +1 -0
- package/dist/structured/standard-schema.js +6 -0
- package/dist/testing/fake-agent.d.ts +32 -0
- package/dist/testing/fake-agent.d.ts.map +1 -0
- package/dist/testing/fake-agent.js +43 -0
- package/dist/testing/fake-clock.d.ts +8 -0
- package/dist/testing/fake-clock.d.ts.map +1 -0
- package/dist/testing/fake-clock.js +44 -0
- package/dist/testing/fake-exec.d.ts +26 -0
- package/dist/testing/fake-exec.d.ts.map +1 -0
- package/dist/testing/fake-exec.js +69 -0
- package/dist/testing/fake-secrets.d.ts +3 -0
- package/dist/testing/fake-secrets.d.ts.map +1 -0
- package/dist/testing/fake-secrets.js +5 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +9 -0
- package/package.json +75 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { LOGIN_EVENT_TYPE, LOGIN_METHOD, } from "../auth/login.js";
|
|
2
|
+
import { AUTH_MATERIAL_KIND, AUTH_STATUS, } from "../auth/material.js";
|
|
3
|
+
import { applyEnvPatch } from "../core/env.js";
|
|
4
|
+
import { AGENT_ERROR_CODE, AgentError, NOT_AVAILABLE_CAUSE, TIMEOUT_KIND, } from "../core/error.js";
|
|
5
|
+
import { redactSecrets } from "../core/redact.js";
|
|
6
|
+
import { OUTPUT_CHANNEL, PROCESS_SIGNAL } from "../core/values.js";
|
|
7
|
+
import { classifyFailure, sharedFailureMatchers } from "./classify.js";
|
|
8
|
+
const DEFAULT_CLI_LOGIN_TIMEOUT_MS = 20 * 60 * 1_000;
|
|
9
|
+
const SIGKILL_ESCALATION_MS = 5_000;
|
|
10
|
+
const RAW_OUTPUT_CAP_CHARS = 10 * 1024 * 1024;
|
|
11
|
+
const OUTPUT_TAIL_CHARS = 2_000;
|
|
12
|
+
const SCAN_BUFFER_CHARS = 12_000;
|
|
13
|
+
const MINUTE_MS = 60_000;
|
|
14
|
+
// Best-effort scanners over the accumulated (ANSI-stripped) CLI output, matching
|
|
15
|
+
// the shape observed in design `an` Phase 0 (codex-cli 0.139.0): a hyphen-grouped
|
|
16
|
+
// uppercase one-time code and an "expires in N minutes" hint. Each takes the
|
|
17
|
+
// FIRST hit; a format drift only suppresses the derived event — exit code 0
|
|
18
|
+
// still yields the outcome.
|
|
19
|
+
const DEVICE_CODE_EXPIRY_PATTERN = /\bexpires in (\d+) minutes?\b/i;
|
|
20
|
+
const DEVICE_CODE_PATTERN = /\b[A-Z0-9]{4,}(?:-[A-Z0-9]{4,})+\b/u;
|
|
21
|
+
const URL_PATTERN = /https?:\/\/[^\s)]+/u;
|
|
22
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: strips ANSI/CSI escape sequences from colorized CLI output.
|
|
23
|
+
const ANSI_PATTERN = /\x1b\[[0-?]*[ -/]*[@-~]/gu;
|
|
24
|
+
function boundedTail(current, text, capChars) {
|
|
25
|
+
const combined = current + text;
|
|
26
|
+
return combined.length > capChars
|
|
27
|
+
? combined.slice(combined.length - capChars)
|
|
28
|
+
: combined;
|
|
29
|
+
}
|
|
30
|
+
function stripAnsi(text) {
|
|
31
|
+
return text.replace(ANSI_PATTERN, "");
|
|
32
|
+
}
|
|
33
|
+
function firstMatch(text, pattern) {
|
|
34
|
+
return text.match(pattern)?.[0] ?? null;
|
|
35
|
+
}
|
|
36
|
+
function deviceCodeExpiresAt(buffer, now) {
|
|
37
|
+
const minutes = buffer.match(DEVICE_CODE_EXPIRY_PATTERN)?.[1];
|
|
38
|
+
return minutes === undefined
|
|
39
|
+
? null
|
|
40
|
+
: new Date(now + Number(minutes) * MINUTE_MS).toISOString();
|
|
41
|
+
}
|
|
42
|
+
const LOGIN_EVENT_PARSERS = {
|
|
43
|
+
[LOGIN_METHOD.oauth]: (buffer) => {
|
|
44
|
+
const url = firstMatch(buffer, URL_PATTERN);
|
|
45
|
+
return url === null ? null : { type: LOGIN_EVENT_TYPE.openUrl, url };
|
|
46
|
+
},
|
|
47
|
+
[LOGIN_METHOD.device]: (buffer, now) => {
|
|
48
|
+
const url = firstMatch(buffer, URL_PATTERN);
|
|
49
|
+
const code = firstMatch(buffer, DEVICE_CODE_PATTERN);
|
|
50
|
+
return url === null || code === null
|
|
51
|
+
? null
|
|
52
|
+
: {
|
|
53
|
+
type: LOGIN_EVENT_TYPE.deviceCode,
|
|
54
|
+
code,
|
|
55
|
+
url,
|
|
56
|
+
expiresAt: deviceCodeExpiresAt(buffer, now),
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
function loginExitError(input) {
|
|
61
|
+
const { command, exit, stdout, stderr, now } = input;
|
|
62
|
+
const classified = classifyFailure(sharedFailureMatchers, {
|
|
63
|
+
stdout,
|
|
64
|
+
stderr,
|
|
65
|
+
exitCode: exit.code,
|
|
66
|
+
signal: exit.signal,
|
|
67
|
+
now,
|
|
68
|
+
});
|
|
69
|
+
if (classified !== null) {
|
|
70
|
+
return new AgentError({
|
|
71
|
+
...classified,
|
|
72
|
+
message: redactSecrets(classified.message),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const signal = exit.signal === null ? "" : ` (signal ${exit.signal})`;
|
|
76
|
+
return new AgentError({
|
|
77
|
+
code: AGENT_ERROR_CODE.failed,
|
|
78
|
+
message: redactSecrets(`Login command "${command}" exited with code ${exit.code ?? "null"}${signal}`),
|
|
79
|
+
data: {
|
|
80
|
+
exitCode: exit.code,
|
|
81
|
+
stdoutTail: redactSecrets(stdout.slice(-OUTPUT_TAIL_CHARS)) || null,
|
|
82
|
+
stderrTail: redactSecrets(stderr.slice(-OUTPUT_TAIL_CHARS)) || null,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function homeDirOutcome(request, status) {
|
|
87
|
+
return {
|
|
88
|
+
material: { kind: AUTH_MATERIAL_KIND.homeDir, path: request.homeDir },
|
|
89
|
+
status,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export async function* cliLoginCommand(options) {
|
|
93
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_CLI_LOGIN_TIMEOUT_MS;
|
|
94
|
+
const env = applyEnvPatch(options.ctx.env, options.envPatch);
|
|
95
|
+
const command = await options.ctx.exec.which(options.command, env);
|
|
96
|
+
if (command === null) {
|
|
97
|
+
throw new AgentError({
|
|
98
|
+
code: AGENT_ERROR_CODE.notAvailable,
|
|
99
|
+
message: `Login command "${options.command}" was not found on PATH`,
|
|
100
|
+
data: { cause: NOT_AVAILABLE_CAUSE.notInstalled },
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const child = options.ctx.exec.spawn({
|
|
104
|
+
command,
|
|
105
|
+
args: [...options.args],
|
|
106
|
+
cwd: options.ctx.location.homeDir,
|
|
107
|
+
env,
|
|
108
|
+
stdin: null,
|
|
109
|
+
});
|
|
110
|
+
const parseEvent = LOGIN_EVENT_PARSERS[options.request.method];
|
|
111
|
+
let stdout = "";
|
|
112
|
+
let stderr = "";
|
|
113
|
+
let scanBuffer = "";
|
|
114
|
+
let emitted = false;
|
|
115
|
+
const { signal } = options.ctx;
|
|
116
|
+
let timedOut = false;
|
|
117
|
+
let killed = false;
|
|
118
|
+
// A box, not a bare `let`: the SIGKILL canceller is assigned only inside the
|
|
119
|
+
// escalation path, and control-flow analysis would otherwise narrow a
|
|
120
|
+
// closure-assigned `let` to `never` at the `finally` read site.
|
|
121
|
+
const cancelKill = { current: null };
|
|
122
|
+
// One kill path shared by the total-timeout and the caller-abort stops:
|
|
123
|
+
// SIGTERM, then SIGKILL after a grace — mirroring the run adapter's onAbort.
|
|
124
|
+
// Idempotent so a timeout racing an abort escalates once.
|
|
125
|
+
const escalateKill = () => {
|
|
126
|
+
if (killed) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
killed = true;
|
|
130
|
+
child.kill(PROCESS_SIGNAL.sigterm);
|
|
131
|
+
cancelKill.current = options.ctx.clock.setTimeout(() => child.kill(PROCESS_SIGNAL.sigkill), SIGKILL_ESCALATION_MS);
|
|
132
|
+
};
|
|
133
|
+
const onAbort = () => escalateKill();
|
|
134
|
+
const cancelTimeout = options.ctx.clock.setTimeout(() => {
|
|
135
|
+
timedOut = true;
|
|
136
|
+
escalateKill();
|
|
137
|
+
}, timeoutMs);
|
|
138
|
+
if (signal.aborted) {
|
|
139
|
+
onAbort();
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
for await (const chunk of child.chunks) {
|
|
146
|
+
if (chunk.channel === OUTPUT_CHANNEL.stdout) {
|
|
147
|
+
stdout = boundedTail(stdout, chunk.text, RAW_OUTPUT_CAP_CHARS);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
stderr = boundedTail(stderr, chunk.text, RAW_OUTPUT_CAP_CHARS);
|
|
151
|
+
}
|
|
152
|
+
yield {
|
|
153
|
+
type: LOGIN_EVENT_TYPE.output,
|
|
154
|
+
channel: chunk.channel,
|
|
155
|
+
chunk: chunk.text,
|
|
156
|
+
};
|
|
157
|
+
if (!emitted) {
|
|
158
|
+
scanBuffer = boundedTail(scanBuffer, stripAnsi(chunk.text), SCAN_BUFFER_CHARS);
|
|
159
|
+
const event = parseEvent(scanBuffer, options.ctx.clock.now());
|
|
160
|
+
if (event !== null) {
|
|
161
|
+
emitted = true;
|
|
162
|
+
yield event;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const exit = await child.exit;
|
|
167
|
+
if (signal.aborted) {
|
|
168
|
+
throw new AgentError({
|
|
169
|
+
code: AGENT_ERROR_CODE.cancelled,
|
|
170
|
+
message: `Login command "${options.command}" was cancelled`,
|
|
171
|
+
data: {
|
|
172
|
+
reason: typeof signal.reason === "string" ? signal.reason : null,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (timedOut) {
|
|
177
|
+
throw new AgentError({
|
|
178
|
+
code: AGENT_ERROR_CODE.timeout,
|
|
179
|
+
message: `Login command "${options.command}" exceeded its total timeout of ${timeoutMs}ms`,
|
|
180
|
+
data: { kind: TIMEOUT_KIND.total, limitMs: timeoutMs },
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (exit.code === 0) {
|
|
184
|
+
return homeDirOutcome(options.request, AUTH_STATUS.valid);
|
|
185
|
+
}
|
|
186
|
+
throw loginExitError({
|
|
187
|
+
command: options.command,
|
|
188
|
+
exit,
|
|
189
|
+
stdout,
|
|
190
|
+
stderr,
|
|
191
|
+
now: options.ctx.clock.now(),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
signal.removeEventListener("abort", onAbort);
|
|
196
|
+
cancelTimeout();
|
|
197
|
+
cancelKill.current?.();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The definition/instance split (design "Kernel"): an `AgentDefinition`
|
|
3
|
+
* is pure description; binding it to a runtime yields an `Agent`.
|
|
4
|
+
* Adapters yield `AgentEvent` and return an `AgentRunOutcome`; the
|
|
5
|
+
* kernel owns `runId`, timing, and the lifecycle bracket.
|
|
6
|
+
*/
|
|
7
|
+
import type { LoginCapability } from "../auth/login.js";
|
|
8
|
+
import type { AuthCapability } from "../auth/material.js";
|
|
9
|
+
import type { Clock, Exec, HttpClient, Logger } from "../core/ports.js";
|
|
10
|
+
import type { AgentEvent, AgentRunOutcome } from "../core/run.js";
|
|
11
|
+
import type { DoctorCapability, McpCapability, SkillsCapability } from "../host/capabilities.js";
|
|
12
|
+
import type { ResolvedRunOptions } from "./options.js";
|
|
13
|
+
/** Resolved (rule 2): the adapter's whole world. */
|
|
14
|
+
export type RunContext = {
|
|
15
|
+
/** The ONE stop channel — caller abort, timeout, and guard violations. */
|
|
16
|
+
signal: AbortSignal;
|
|
17
|
+
/** Fully layered: baseEnv → auth patch → options.env. */
|
|
18
|
+
env: Record<string, string>;
|
|
19
|
+
exec: Exec;
|
|
20
|
+
http: HttpClient;
|
|
21
|
+
/** For adapter-owned time mechanics: kill escalation, completion grace. */
|
|
22
|
+
clock: Clock;
|
|
23
|
+
/** Pre-scoped to the run by the kernel. */
|
|
24
|
+
logger: Logger;
|
|
25
|
+
};
|
|
26
|
+
export type AgentRun = AsyncGenerator<AgentEvent, AgentRunOutcome, void>;
|
|
27
|
+
export interface AgentDefinition<Id extends string = string> {
|
|
28
|
+
id: Id;
|
|
29
|
+
run(options: ResolvedRunOptions, ctx: RunContext): AgentRun;
|
|
30
|
+
auth?: AuthCapability;
|
|
31
|
+
login?: LoginCapability;
|
|
32
|
+
doctor?: DoctorCapability;
|
|
33
|
+
skills?: SkillsCapability;
|
|
34
|
+
mcp?: McpCapability;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Identity helper for hand-rolled agents: pins the `Id` literal and
|
|
38
|
+
* type-checks the definition in place. No runtime behavior.
|
|
39
|
+
*/
|
|
40
|
+
export declare function defineAgent<const Id extends string>(definition: AgentDefinition<Id>): AgentDefinition<Id>;
|
|
41
|
+
//# sourceMappingURL=definition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/kernel/definition.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,oDAAoD;AACpD,MAAM,MAAM,UAAU,GAAG;IACvB,0EAA0E;IAC1E,MAAM,EAAE,WAAW,CAAC;IACpB,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,2EAA2E;IAC3E,KAAK,EAAE,KAAK,CAAC;IACb,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,cAAc,CAAC,UAAU,EAAE,eAAe,EAAE,IAAI,CAAC,CAAC;AAEzE,MAAM,WAAW,eAAe,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM;IACzD,EAAE,EAAE,EAAE,CAAC;IACP,GAAG,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5D,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,GAAG,CAAC,EAAE,aAAa,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EACjD,UAAU,EAAE,eAAe,CAAC,EAAE,CAAC,GAC9B,eAAe,CAAC,EAAE,CAAC,CAErB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The definition/instance split (design "Kernel"): an `AgentDefinition`
|
|
3
|
+
* is pure description; binding it to a runtime yields an `Agent`.
|
|
4
|
+
* Adapters yield `AgentEvent` and return an `AgentRunOutcome`; the
|
|
5
|
+
* kernel owns `runId`, timing, and the lifecycle bracket.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Identity helper for hand-rolled agents: pins the `Id` literal and
|
|
9
|
+
* type-checks the definition in place. No runtime behavior.
|
|
10
|
+
*/
|
|
11
|
+
export function defineAgent(definition) {
|
|
12
|
+
return definition;
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type EnvTemplate = string;
|
|
2
|
+
declare const ENV_TEMPLATE_FAILURE_REASON: {
|
|
3
|
+
readonly missing: "missing";
|
|
4
|
+
readonly empty: "empty";
|
|
5
|
+
};
|
|
6
|
+
export declare function referencedEnvNames(value: unknown): readonly string[];
|
|
7
|
+
export type EnvTemplateResolution = {
|
|
8
|
+
readonly ok: true;
|
|
9
|
+
readonly value: string;
|
|
10
|
+
} | {
|
|
11
|
+
readonly ok: false;
|
|
12
|
+
readonly reason: (typeof ENV_TEMPLATE_FAILURE_REASON)[keyof typeof ENV_TEMPLATE_FAILURE_REASON];
|
|
13
|
+
readonly envName: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function resolveEnvTemplate(value: EnvTemplate, env: Readonly<Record<string, string>>): EnvTemplateResolution;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=env-template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-template.d.ts","sourceRoot":"","sources":["../../src/kernel/env-template.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAIjC,QAAA,MAAM,2BAA2B;;;CAGvB,CAAC;AAEX,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAIpE;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC7C;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,OAAO,2BAA2B,CAAC,CAAC;IAChG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACpC,qBAAqB,CA4BvB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { AGENT_ERROR_CODE, AgentError } from "../core/error.js";
|
|
2
|
+
const ENV_TEMPLATE_EXPRESSION = /\$\{([^}]*)\}/gu;
|
|
3
|
+
const ENV_TEMPLATE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
4
|
+
const ENV_TEMPLATE_FAILURE_REASON = {
|
|
5
|
+
missing: "missing",
|
|
6
|
+
empty: "empty",
|
|
7
|
+
};
|
|
8
|
+
export function referencedEnvNames(value) {
|
|
9
|
+
const names = new Set();
|
|
10
|
+
collectReferencedEnvNames(value, names);
|
|
11
|
+
return [...names];
|
|
12
|
+
}
|
|
13
|
+
export function resolveEnvTemplate(value, env) {
|
|
14
|
+
assertValidEnvTemplate(value);
|
|
15
|
+
let failed = null;
|
|
16
|
+
const resolved = value.replace(ENV_TEMPLATE_EXPRESSION, (expression, key) => {
|
|
17
|
+
if (!ENV_TEMPLATE_NAME.test(String(key))) {
|
|
18
|
+
throw invalidTemplateError(expression);
|
|
19
|
+
}
|
|
20
|
+
const replacement = env[String(key)];
|
|
21
|
+
if (replacement === undefined) {
|
|
22
|
+
failed = {
|
|
23
|
+
ok: false,
|
|
24
|
+
reason: ENV_TEMPLATE_FAILURE_REASON.missing,
|
|
25
|
+
envName: String(key),
|
|
26
|
+
};
|
|
27
|
+
return expression;
|
|
28
|
+
}
|
|
29
|
+
if (replacement.length === 0) {
|
|
30
|
+
failed = {
|
|
31
|
+
ok: false,
|
|
32
|
+
reason: ENV_TEMPLATE_FAILURE_REASON.empty,
|
|
33
|
+
envName: String(key),
|
|
34
|
+
};
|
|
35
|
+
return expression;
|
|
36
|
+
}
|
|
37
|
+
return replacement;
|
|
38
|
+
});
|
|
39
|
+
return failed ?? { ok: true, value: resolved };
|
|
40
|
+
}
|
|
41
|
+
function collectReferencedEnvNames(value, names) {
|
|
42
|
+
if (typeof value === "string") {
|
|
43
|
+
assertValidEnvTemplate(value);
|
|
44
|
+
for (const match of value.matchAll(ENV_TEMPLATE_EXPRESSION)) {
|
|
45
|
+
const expression = match[0] ?? "";
|
|
46
|
+
const name = match[1] ?? "";
|
|
47
|
+
if (!ENV_TEMPLATE_NAME.test(name)) {
|
|
48
|
+
throw invalidTemplateError(expression);
|
|
49
|
+
}
|
|
50
|
+
names.add(name);
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
for (const item of value) {
|
|
56
|
+
collectReferencedEnvNames(item, names);
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (value !== null && typeof value === "object") {
|
|
61
|
+
for (const item of Object.values(value)) {
|
|
62
|
+
collectReferencedEnvNames(item, names);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function assertValidEnvTemplate(value) {
|
|
67
|
+
const unresolved = value.replace(ENV_TEMPLATE_EXPRESSION, "");
|
|
68
|
+
if (unresolved.includes("${")) {
|
|
69
|
+
throw invalidTemplateError("${");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function invalidTemplateError(expression) {
|
|
73
|
+
return new AgentError({
|
|
74
|
+
code: AGENT_ERROR_CODE.invalidInput,
|
|
75
|
+
message: `Invalid environment template: ${expression}`,
|
|
76
|
+
data: { reason: `invalid environment template: ${expression}` },
|
|
77
|
+
});
|
|
78
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bound capability facets (design "Architecture"): the runtime owns the
|
|
3
|
+
* effectful context parts and binds them once; call sites pass only a
|
|
4
|
+
* `HostLocation`. Keyed-record dispatch is enforced here — a missing
|
|
5
|
+
* handler key is a typed `invalid_input`, never a silent fallback.
|
|
6
|
+
*/
|
|
7
|
+
import { type LoginCapability, type LoginHandle, type LoginMethod, type LoginRequest } from "../auth/login.js";
|
|
8
|
+
import type { AuthCapability, AuthMaterial, AuthMaterialDescriptor, AuthMaterialKind, AuthStatus } from "../auth/material.js";
|
|
9
|
+
import type { EnvPatch } from "../core/env.js";
|
|
10
|
+
import type { Clock, Exec, HttpClient, Logger, SecretResolvers } from "../core/ports.js";
|
|
11
|
+
import type { DoctorCapability, DoctorReport, InstalledSkill, InstallOptions, InstallOutcome, McpCapability, McpScope, McpServerSpec, McpServerState, SkillSource, SkillsCapability } from "../host/capabilities.js";
|
|
12
|
+
import type { HostLocation } from "../host/context.js";
|
|
13
|
+
export type AgentAuth = {
|
|
14
|
+
supports(kind: AuthMaterialKind): boolean;
|
|
15
|
+
/** Supported material kinds in canonical order (empty if none). */
|
|
16
|
+
kinds(): AuthMaterialKind[];
|
|
17
|
+
/** Per-kind introspection for setup UIs (empty if none). */
|
|
18
|
+
describe(): AuthMaterialDescriptor[];
|
|
19
|
+
toEnv(material: AuthMaterial): Promise<EnvPatch>;
|
|
20
|
+
check(material: AuthMaterial, location: HostLocation): Promise<AuthStatus>;
|
|
21
|
+
};
|
|
22
|
+
export type AgentDoctor = {
|
|
23
|
+
probe(location: HostLocation): Promise<DoctorReport>;
|
|
24
|
+
};
|
|
25
|
+
export type AgentLogin = {
|
|
26
|
+
/** Supported sign-in methods in canonical order (empty if none). */
|
|
27
|
+
methods(): LoginMethod[];
|
|
28
|
+
supports(method: LoginMethod): boolean;
|
|
29
|
+
start(request: LoginRequest, location: HostLocation): LoginHandle;
|
|
30
|
+
};
|
|
31
|
+
export type AgentSkills = {
|
|
32
|
+
list(location: HostLocation): Promise<InstalledSkill[]>;
|
|
33
|
+
install(location: HostLocation, source: SkillSource, options?: InstallOptions): Promise<InstallOutcome>;
|
|
34
|
+
};
|
|
35
|
+
export type AgentMcp = {
|
|
36
|
+
list(location: HostLocation): Promise<McpServerState[]>;
|
|
37
|
+
add(location: HostLocation, name: string, spec: McpServerSpec, options: {
|
|
38
|
+
scope: McpScope;
|
|
39
|
+
}): Promise<InstallOutcome>;
|
|
40
|
+
remove(location: HostLocation, name: string, options: {
|
|
41
|
+
scope: McpScope;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
};
|
|
44
|
+
export type FacetBinding = {
|
|
45
|
+
agentId: string;
|
|
46
|
+
env: Record<string, string>;
|
|
47
|
+
exec: Exec;
|
|
48
|
+
http: HttpClient;
|
|
49
|
+
clock: Clock;
|
|
50
|
+
logger: Logger;
|
|
51
|
+
secrets: SecretResolvers;
|
|
52
|
+
};
|
|
53
|
+
export declare function bindAuth(capability: AuthCapability, binding: FacetBinding): AgentAuth;
|
|
54
|
+
export declare function bindLogin(capability: LoginCapability, binding: FacetBinding): AgentLogin;
|
|
55
|
+
export declare function bindDoctor(capability: DoctorCapability, binding: FacetBinding): AgentDoctor;
|
|
56
|
+
export declare function bindSkills(capability: SkillsCapability, binding: FacetBinding): AgentSkills;
|
|
57
|
+
export declare function bindMcp(capability: McpCapability, binding: FacetBinding): AgentMcp;
|
|
58
|
+
//# sourceMappingURL=facets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facets.d.ts","sourceRoot":"","sources":["../../src/kernel/facets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bound capability facets (design "Architecture"): the runtime owns the
|
|
3
|
+
* effectful context parts and binds them once; call sites pass only a
|
|
4
|
+
* `HostLocation`. Keyed-record dispatch is enforced here — a missing
|
|
5
|
+
* handler key is a typed `invalid_input`, never a silent fallback.
|
|
6
|
+
*/
|
|
7
|
+
import { LOGIN_METHOD, } from "../auth/login.js";
|
|
8
|
+
import { AUTH_MATERIAL_KIND, AUTH_STATUS, authMaterialRequiresSecret, } from "../auth/material.js";
|
|
9
|
+
import { AGENT_ERROR_CODE, AgentError } from "../core/error.js";
|
|
10
|
+
import { resolveHostLocation } from "../host/context.js";
|
|
11
|
+
import { validateSpecSecrets } from "../host/validate-spec.js";
|
|
12
|
+
import { keyedHandler, requireKeyedHandler } from "./keyed.js";
|
|
13
|
+
import { startLogin } from "./login.js";
|
|
14
|
+
function hostContext(binding, location) {
|
|
15
|
+
return {
|
|
16
|
+
location: resolveHostLocation(location),
|
|
17
|
+
env: binding.env,
|
|
18
|
+
exec: binding.exec,
|
|
19
|
+
http: binding.http,
|
|
20
|
+
clock: binding.clock,
|
|
21
|
+
logger: binding.logger,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Supported kinds in canonical declaration order, not the record's key order. */
|
|
25
|
+
function supportedKinds(materials) {
|
|
26
|
+
return Object.values(AUTH_MATERIAL_KIND).filter((kind) => materials[kind] !== undefined);
|
|
27
|
+
}
|
|
28
|
+
function supportedLoginMethods(capability) {
|
|
29
|
+
return Object.values(LOGIN_METHOD).filter((method) => capability.methods[method] !== undefined);
|
|
30
|
+
}
|
|
31
|
+
export function bindAuth(capability, binding) {
|
|
32
|
+
const requireHandler = (kind) => requireKeyedHandler(capability.materials, kind, binding.agentId, `auth material "${kind}"`);
|
|
33
|
+
return {
|
|
34
|
+
supports: (kind) => keyedHandler(capability.materials, kind) !== null,
|
|
35
|
+
kinds: () => supportedKinds(capability.materials),
|
|
36
|
+
describe: () => supportedKinds(capability.materials).map((kind) => ({
|
|
37
|
+
kind,
|
|
38
|
+
requiresSecret: authMaterialRequiresSecret(kind),
|
|
39
|
+
canProbe: capability.materials[kind]?.check !== undefined,
|
|
40
|
+
})),
|
|
41
|
+
toEnv: (material) => requireHandler(material.kind).toEnv(material, binding.secrets),
|
|
42
|
+
check: (material, location) => {
|
|
43
|
+
const handler = requireHandler(material.kind);
|
|
44
|
+
if (handler.check === undefined) {
|
|
45
|
+
return Promise.resolve(AUTH_STATUS.unknown);
|
|
46
|
+
}
|
|
47
|
+
return handler.check(material, hostContext(binding, location));
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function bindLogin(capability, binding) {
|
|
52
|
+
const requireHandler = (method) => requireKeyedHandler(capability.methods, method, binding.agentId, `login method "${method}"`);
|
|
53
|
+
return {
|
|
54
|
+
methods: () => supportedLoginMethods(capability),
|
|
55
|
+
supports: (method) => keyedHandler(capability.methods, method) !== null,
|
|
56
|
+
start: (request, location) => startLogin({
|
|
57
|
+
agentId: binding.agentId,
|
|
58
|
+
request,
|
|
59
|
+
ctx: hostContext(binding, location),
|
|
60
|
+
handler: requireHandler(request.method),
|
|
61
|
+
// Strip the signal off the request into the driver's stop channel,
|
|
62
|
+
// exactly as `bindAgent.run` does with `RunOptions.signal`.
|
|
63
|
+
callerSignal: request.signal ?? null,
|
|
64
|
+
}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function bindDoctor(capability, binding) {
|
|
68
|
+
return {
|
|
69
|
+
probe: (location) => capability.probe(hostContext(binding, location)),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function bindSkills(capability, binding) {
|
|
73
|
+
return {
|
|
74
|
+
list: (location) => capability.list(hostContext(binding, location)),
|
|
75
|
+
install: (location, source, options) => {
|
|
76
|
+
const handler = requireKeyedHandler(capability.install, source.kind, binding.agentId, `skill source "${source.kind}"`);
|
|
77
|
+
return handler(hostContext(binding, location), source, options ?? {});
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function bindMcp(capability, binding) {
|
|
82
|
+
return {
|
|
83
|
+
list: (location) => capability.list(hostContext(binding, location)),
|
|
84
|
+
add: (location, name, spec, options) => {
|
|
85
|
+
const issues = validateSpecSecrets(spec);
|
|
86
|
+
if (issues.length > 0) {
|
|
87
|
+
throw new AgentError({
|
|
88
|
+
code: AGENT_ERROR_CODE.invalidInput,
|
|
89
|
+
message: "MCP server spec contains a literal or forbidden secret",
|
|
90
|
+
data: {
|
|
91
|
+
reason: issues
|
|
92
|
+
.map((issue) => `${issue.path.join(".")}: ${issue.code}`)
|
|
93
|
+
.join("; "),
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const handler = requireKeyedHandler(capability.add, spec.transport, binding.agentId, `MCP transport "${spec.transport}"`);
|
|
98
|
+
return handler(hostContext(binding, location), name, spec, options);
|
|
99
|
+
},
|
|
100
|
+
remove: (location, name, options) => capability.remove(hostContext(binding, location), name, options),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards are kernel-owned event observers (design "Limits and guards"):
|
|
3
|
+
* the kernel feeds them every RunEvent, polls `violation()` on its own
|
|
4
|
+
* timer so time-based guards fire between events, and trips the run's
|
|
5
|
+
* abort on violation. Factories receive `{ clock }` — no `Date.now()`
|
|
6
|
+
* in guards, so `noProgress` stays deterministic under a fake clock.
|
|
7
|
+
*/
|
|
8
|
+
import type { GuardViolation } from "../core/error.js";
|
|
9
|
+
import type { Clock } from "../core/ports.js";
|
|
10
|
+
import type { RunEvent } from "../core/run.js";
|
|
11
|
+
export type GuardContext = {
|
|
12
|
+
clock: Clock;
|
|
13
|
+
};
|
|
14
|
+
export interface Guard {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
observe(event: RunEvent): void;
|
|
17
|
+
violation(): GuardViolation | null;
|
|
18
|
+
}
|
|
19
|
+
export type GuardFactory = (ctx: GuardContext) => Guard;
|
|
20
|
+
export declare function maxTurns(limit: number): GuardFactory;
|
|
21
|
+
export declare function maxToolCalls(limit: number): GuardFactory;
|
|
22
|
+
export declare function repeatedOutput(times: number): GuardFactory;
|
|
23
|
+
export declare function noProgress(ms: number): GuardFactory;
|
|
24
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/kernel/guard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAE5C,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/B,SAAS,IAAI,cAAc,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,KAAK,CAAC;AA0BxD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAQpD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAQxD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAsB1D;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,CAiBnD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards are kernel-owned event observers (design "Limits and guards"):
|
|
3
|
+
* the kernel feeds them every RunEvent, polls `violation()` on its own
|
|
4
|
+
* timer so time-based guards fire between events, and trips the run's
|
|
5
|
+
* abort on violation. Factories receive `{ clock }` — no `Date.now()`
|
|
6
|
+
* in guards, so `noProgress` stays deterministic under a fake clock.
|
|
7
|
+
*/
|
|
8
|
+
import { AGENT_EVENT_TYPE } from "../core/run.js";
|
|
9
|
+
const MAX_TURNS_GUARD_ID = "max-turns";
|
|
10
|
+
const MAX_TOOL_CALLS_GUARD_ID = "max-tool-calls";
|
|
11
|
+
const REPEATED_OUTPUT_GUARD_ID = "repeated-output";
|
|
12
|
+
const NO_PROGRESS_GUARD_ID = "no-progress";
|
|
13
|
+
function countingGuard(id, limit, counts, describe) {
|
|
14
|
+
let count = 0;
|
|
15
|
+
return {
|
|
16
|
+
id,
|
|
17
|
+
observe: (event) => {
|
|
18
|
+
if (counts(event)) {
|
|
19
|
+
count += 1;
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
violation: () => count > limit ? { guardId: id, reason: describe(limit) } : null,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function maxTurns(limit) {
|
|
26
|
+
return () => countingGuard(MAX_TURNS_GUARD_ID, limit, (event) => event.type === AGENT_EVENT_TYPE.turn, (max) => `exceeded ${max} turns`);
|
|
27
|
+
}
|
|
28
|
+
export function maxToolCalls(limit) {
|
|
29
|
+
return () => countingGuard(MAX_TOOL_CALLS_GUARD_ID, limit, (event) => event.type === AGENT_EVENT_TYPE.tool, (max) => `exceeded ${max} tool calls`);
|
|
30
|
+
}
|
|
31
|
+
export function repeatedOutput(times) {
|
|
32
|
+
return () => {
|
|
33
|
+
let lastText = null;
|
|
34
|
+
let repeats = 0;
|
|
35
|
+
return {
|
|
36
|
+
id: REPEATED_OUTPUT_GUARD_ID,
|
|
37
|
+
observe: (event) => {
|
|
38
|
+
if (event.type !== AGENT_EVENT_TYPE.message) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
repeats = event.text === lastText ? repeats + 1 : 1;
|
|
42
|
+
lastText = event.text;
|
|
43
|
+
},
|
|
44
|
+
violation: () => repeats >= times
|
|
45
|
+
? {
|
|
46
|
+
guardId: REPEATED_OUTPUT_GUARD_ID,
|
|
47
|
+
reason: `identical assistant message repeated ${repeats} times`,
|
|
48
|
+
}
|
|
49
|
+
: null,
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function noProgress(ms) {
|
|
54
|
+
return ({ clock }) => {
|
|
55
|
+
let lastEventAt = clock.now();
|
|
56
|
+
return {
|
|
57
|
+
id: NO_PROGRESS_GUARD_ID,
|
|
58
|
+
observe: () => {
|
|
59
|
+
lastEventAt = clock.now();
|
|
60
|
+
},
|
|
61
|
+
violation: () => clock.now() - lastEventAt >= ms
|
|
62
|
+
? {
|
|
63
|
+
guardId: NO_PROGRESS_GUARD_ID,
|
|
64
|
+
reason: `no events for ${ms}ms`,
|
|
65
|
+
}
|
|
66
|
+
: null,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunHandle internals: a bounded-replay broadcast and an awaitable
|
|
3
|
+
* handle (`then` delegates to `result`). Not consuming events never
|
|
4
|
+
* blocks the run. Subscribers attached while the run streams receive
|
|
5
|
+
* every event live; a LATE subscriber gets the retained replay window —
|
|
6
|
+
* bounded by payload bytes so a verbose run cannot hold its whole
|
|
7
|
+
* output in memory a second time (the capped raw transcript already
|
|
8
|
+
* lives on the result).
|
|
9
|
+
*/
|
|
10
|
+
import type { RunEvent, RunHandle, RunResult } from "../core/run.js";
|
|
11
|
+
type EventCost<Event> = (event: Event) => number;
|
|
12
|
+
export declare class EventBroadcast<Event = RunEvent> {
|
|
13
|
+
private readonly replayBudgetBytes;
|
|
14
|
+
private readonly eventCost;
|
|
15
|
+
private history;
|
|
16
|
+
private historyStart;
|
|
17
|
+
private retainedBytes;
|
|
18
|
+
private readonly subscribers;
|
|
19
|
+
private ended;
|
|
20
|
+
constructor(replayBudgetBytes?: number, eventCost?: EventCost<Event>);
|
|
21
|
+
push(event: Event): void;
|
|
22
|
+
/** The most recent event always survives, however large. */
|
|
23
|
+
private evictOverBudget;
|
|
24
|
+
end(): void;
|
|
25
|
+
get events(): AsyncIterable<Event>;
|
|
26
|
+
}
|
|
27
|
+
export declare function createRunHandle(runId: string, broadcast: EventBroadcast<RunEvent>, result: Promise<RunResult>): RunHandle;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=handle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../../src/kernel/handle.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAKrE,KAAK,SAAS,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC;AAYjD,qBAAa,cAAc,CAAC,KAAK,GAAG,QAAQ;IAQxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAR5B,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgC;IAC5D,OAAO,CAAC,KAAK,CAAS;gBAGH,iBAAiB,SAA8B,EAC/C,SAAS,GAAE,SAAS,CAAC,KAAK,CAAkC;IAG/E,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAYxB,4DAA4D;IAC5D,OAAO,CAAC,eAAe;IAkBvB,GAAG,IAAI,IAAI;IAQX,IAAI,MAAM,IAAI,aAAa,CAAC,KAAK,CAAC,CAejC;CACF;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,cAAc,CAAC,QAAQ,CAAC,EACnC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,GACzB,SAAS,CAUX"}
|