@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,216 @@
|
|
|
1
|
+
import { AGENT_ERROR_CODE, AgentError, AUTH_FAILURE_REASON, isAgentError, NOT_AVAILABLE_CAUSE, } from "../../core/error.js";
|
|
2
|
+
import { redactSecrets } from "../../core/redact.js";
|
|
3
|
+
import { parseCooldownHint } from "../../kernel/classify.js";
|
|
4
|
+
import { createDefaultOpenAiCompatibleResponseDecoders, } from "./response.js";
|
|
5
|
+
const CHAT_COMPLETIONS_PATH = "chat/completions";
|
|
6
|
+
const HTTP_METHOD_POST = "POST";
|
|
7
|
+
const OPENAI_COMPATIBLE_HTTP_HEADER = {
|
|
8
|
+
authorizationCanonical: "Authorization",
|
|
9
|
+
authorizationLookup: "authorization",
|
|
10
|
+
contentType: "content-type",
|
|
11
|
+
retryAfter: "retry-after",
|
|
12
|
+
};
|
|
13
|
+
const HTTP_CONTENT_TYPE = {
|
|
14
|
+
json: "application/json",
|
|
15
|
+
};
|
|
16
|
+
const HTTP_STATUS = {
|
|
17
|
+
unauthorized: 401,
|
|
18
|
+
forbidden: 403,
|
|
19
|
+
rateLimited: 429,
|
|
20
|
+
};
|
|
21
|
+
const AUTH_REASON_BY_STATUS = {
|
|
22
|
+
[HTTP_STATUS.unauthorized]: AUTH_FAILURE_REASON.unauthorized,
|
|
23
|
+
[HTTP_STATUS.forbidden]: AUTH_FAILURE_REASON.forbidden,
|
|
24
|
+
};
|
|
25
|
+
const RETRY_AFTER_COOLDOWN_HINT_PREFIX = "retry-after: ";
|
|
26
|
+
const CHAT_COMPLETION_REQUEST_FIELD = {
|
|
27
|
+
messages: "messages",
|
|
28
|
+
model: "model",
|
|
29
|
+
stream: "stream",
|
|
30
|
+
};
|
|
31
|
+
const CHAT_COMPLETION_ROLE = {
|
|
32
|
+
user: "user",
|
|
33
|
+
};
|
|
34
|
+
const CHAT_COMPLETION_STREAMING = true;
|
|
35
|
+
const DEFAULT_MAX_ATTEMPTS = 2;
|
|
36
|
+
const STDERR_TAIL_CHARS = 2_000;
|
|
37
|
+
export class FetchOpenAiCompatibleEngine {
|
|
38
|
+
decoders;
|
|
39
|
+
retryPolicy;
|
|
40
|
+
failurePolicy;
|
|
41
|
+
constructor(dependencies = {}) {
|
|
42
|
+
this.decoders =
|
|
43
|
+
dependencies.decoders ?? createDefaultOpenAiCompatibleResponseDecoders();
|
|
44
|
+
this.retryPolicy =
|
|
45
|
+
dependencies.retryPolicy ?? new DefaultOpenAiCompatibleRetryPolicy();
|
|
46
|
+
this.failurePolicy =
|
|
47
|
+
dependencies.failurePolicy ??
|
|
48
|
+
new DefaultOpenAiCompatibleHttpFailurePolicy();
|
|
49
|
+
}
|
|
50
|
+
async doctorChecks() {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
async *run(config, options, ctx) {
|
|
54
|
+
let lastError = null;
|
|
55
|
+
for (let attempt = 1; attempt <= this.retryPolicy.maxAttempts; attempt += 1) {
|
|
56
|
+
// Once a decoder has yielded an event, the attempt is no longer
|
|
57
|
+
// retryable: a second attempt would re-stream and duplicate output.
|
|
58
|
+
let emitted = false;
|
|
59
|
+
try {
|
|
60
|
+
const response = await ctx.http.fetch(chatCompletionsUrl(config.baseUrl), {
|
|
61
|
+
method: HTTP_METHOD_POST,
|
|
62
|
+
headers: requestHeaders(config),
|
|
63
|
+
body: JSON.stringify(requestBody(config, options)),
|
|
64
|
+
signal: ctx.signal,
|
|
65
|
+
});
|
|
66
|
+
const failure = await this.failurePolicy.errorFor(response, ctx);
|
|
67
|
+
if (failure !== null) {
|
|
68
|
+
throw failure;
|
|
69
|
+
}
|
|
70
|
+
const decoded = this.decoderFor(response).decode(response, config.model);
|
|
71
|
+
for (;;) {
|
|
72
|
+
const next = await decoded.next();
|
|
73
|
+
if (next.done) {
|
|
74
|
+
return next.value;
|
|
75
|
+
}
|
|
76
|
+
emitted = true;
|
|
77
|
+
yield next.value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const normalized = normalizeRunError(error);
|
|
82
|
+
if (emitted || !this.retryPolicy.shouldRetry(normalized, attempt)) {
|
|
83
|
+
throw normalized;
|
|
84
|
+
}
|
|
85
|
+
lastError = normalized;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
throw lastError ?? unreachableError();
|
|
89
|
+
}
|
|
90
|
+
decoderFor(response) {
|
|
91
|
+
const decoder = this.decoders.find((candidate) => candidate.supports(response));
|
|
92
|
+
if (decoder === undefined) {
|
|
93
|
+
throw new AgentError({
|
|
94
|
+
code: AGENT_ERROR_CODE.failed,
|
|
95
|
+
message: "OpenAI-compatible endpoint returned an unsupported response",
|
|
96
|
+
data: { exitCode: null, stdoutTail: null, stderrTail: null },
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return decoder;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export class DefaultOpenAiCompatibleRetryPolicy {
|
|
103
|
+
maxAttempts = DEFAULT_MAX_ATTEMPTS;
|
|
104
|
+
shouldRetry(error, attempt) {
|
|
105
|
+
return attempt < this.maxAttempts && isRetryableRunError(error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export class DefaultOpenAiCompatibleHttpFailurePolicy {
|
|
109
|
+
handlers = [
|
|
110
|
+
authFailureHandler,
|
|
111
|
+
quotaFailureHandler,
|
|
112
|
+
genericHttpFailureHandler,
|
|
113
|
+
];
|
|
114
|
+
async errorFor(response, ctx) {
|
|
115
|
+
const handler = this.handlers.find((candidate) => candidate.matches(response));
|
|
116
|
+
return handler === undefined ? null : handler.errorFor(response, ctx);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const authFailureHandler = {
|
|
120
|
+
matches: (response) => isAuthHttpStatus(response.status),
|
|
121
|
+
errorFor: async (response) => new AgentError({
|
|
122
|
+
code: AGENT_ERROR_CODE.auth,
|
|
123
|
+
message: `OpenAI-compatible endpoint rejected authentication with HTTP ${response.status}`,
|
|
124
|
+
data: {
|
|
125
|
+
reason: AUTH_REASON_BY_STATUS[response.status],
|
|
126
|
+
detail: `HTTP ${response.status}`,
|
|
127
|
+
},
|
|
128
|
+
}),
|
|
129
|
+
};
|
|
130
|
+
const quotaFailureHandler = {
|
|
131
|
+
matches: (response) => response.status === HTTP_STATUS.rateLimited,
|
|
132
|
+
errorFor: async (response, ctx) => {
|
|
133
|
+
const retryAfter = response.headers.get(OPENAI_COMPATIBLE_HTTP_HEADER.retryAfter);
|
|
134
|
+
return new AgentError({
|
|
135
|
+
code: AGENT_ERROR_CODE.quota,
|
|
136
|
+
message: "OpenAI-compatible endpoint reported a rate limit",
|
|
137
|
+
data: {
|
|
138
|
+
cooldownUntil: retryAfter === null
|
|
139
|
+
? null
|
|
140
|
+
: parseCooldownHint(`${RETRY_AFTER_COOLDOWN_HINT_PREFIX}${retryAfter}`, ctx.clock.now()),
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
const genericHttpFailureHandler = {
|
|
146
|
+
matches: (response) => !response.ok,
|
|
147
|
+
errorFor: async (response) => {
|
|
148
|
+
const body = await response.text().catch(() => "");
|
|
149
|
+
const redactedBody = redactSecrets(body);
|
|
150
|
+
return new AgentError({
|
|
151
|
+
code: AGENT_ERROR_CODE.failed,
|
|
152
|
+
message: `OpenAI-compatible endpoint failed with HTTP ${response.status}`,
|
|
153
|
+
data: {
|
|
154
|
+
exitCode: null,
|
|
155
|
+
stdoutTail: null,
|
|
156
|
+
stderrTail: redactedBody === "" ? null : redactedBody.slice(-STDERR_TAIL_CHARS),
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
function chatCompletionsUrl(baseUrl) {
|
|
162
|
+
const normalized = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
163
|
+
return new URL(CHAT_COMPLETIONS_PATH, normalized).toString();
|
|
164
|
+
}
|
|
165
|
+
function requestHeaders(config) {
|
|
166
|
+
const headers = {
|
|
167
|
+
[OPENAI_COMPATIBLE_HTTP_HEADER.contentType]: HTTP_CONTENT_TYPE.json,
|
|
168
|
+
...config.headers,
|
|
169
|
+
};
|
|
170
|
+
if (config.apiKey !== null &&
|
|
171
|
+
!Object.keys(headers).some((name) => name.toLowerCase() ===
|
|
172
|
+
OPENAI_COMPATIBLE_HTTP_HEADER.authorizationLookup)) {
|
|
173
|
+
headers[OPENAI_COMPATIBLE_HTTP_HEADER.authorizationCanonical] =
|
|
174
|
+
`Bearer ${config.apiKey}`;
|
|
175
|
+
}
|
|
176
|
+
return headers;
|
|
177
|
+
}
|
|
178
|
+
function requestBody(config, options) {
|
|
179
|
+
return {
|
|
180
|
+
...config.requestParams,
|
|
181
|
+
[CHAT_COMPLETION_REQUEST_FIELD.model]: options.model ?? config.model,
|
|
182
|
+
[CHAT_COMPLETION_REQUEST_FIELD.messages]: [
|
|
183
|
+
{ role: CHAT_COMPLETION_ROLE.user, content: options.prompt },
|
|
184
|
+
],
|
|
185
|
+
[CHAT_COMPLETION_REQUEST_FIELD.stream]: CHAT_COMPLETION_STREAMING,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function isAuthHttpStatus(status) {
|
|
189
|
+
return Object.hasOwn(AUTH_REASON_BY_STATUS, status);
|
|
190
|
+
}
|
|
191
|
+
function normalizeRunError(error) {
|
|
192
|
+
if (isAgentError(error)) {
|
|
193
|
+
return error;
|
|
194
|
+
}
|
|
195
|
+
return new AgentError({
|
|
196
|
+
code: AGENT_ERROR_CODE.notAvailable,
|
|
197
|
+
message: error instanceof Error ? error.message : String(error),
|
|
198
|
+
data: { cause: NOT_AVAILABLE_CAUSE.unreachable },
|
|
199
|
+
cause: error,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function isRetryableRunError(error) {
|
|
203
|
+
if (isAgentError(error, AGENT_ERROR_CODE.failed)) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
return (isAgentError(error, AGENT_ERROR_CODE.notAvailable) &&
|
|
207
|
+
error.data.cause === NOT_AVAILABLE_CAUSE.unreachable);
|
|
208
|
+
}
|
|
209
|
+
function unreachableError() {
|
|
210
|
+
return new AgentError({
|
|
211
|
+
code: AGENT_ERROR_CODE.failed,
|
|
212
|
+
message: "OpenAI-compatible endpoint failed without an error",
|
|
213
|
+
data: { exitCode: null, stdoutTail: null, stderrTail: null },
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
export const defaultOpenAiCompatibleEngine = new FetchOpenAiCompatibleEngine();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type DoctorCheck } from "../../../host/capabilities.js";
|
|
2
|
+
import type { OpenAiCompatibleEngine } from "../engine.js";
|
|
3
|
+
import { type PiModuleLoader } from "./module.js";
|
|
4
|
+
export type PiSessionEngineOptions = {
|
|
5
|
+
readonly agentDir: string;
|
|
6
|
+
readonly module?: PiModuleLoader;
|
|
7
|
+
/**
|
|
8
|
+
* Context window (tokens) advertised to pi for the configured model. Pi
|
|
9
|
+
* uses it for compaction thresholds; the OpenAI-compatible config carries
|
|
10
|
+
* no model-capability metadata, so the default is a deliberate placeholder
|
|
11
|
+
* (PI_DEFAULT_CONTEXT_WINDOW) — set this when the real window differs.
|
|
12
|
+
*/
|
|
13
|
+
readonly contextWindow?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Max output tokens advertised to pi for the configured model; pi passes
|
|
16
|
+
* it to the provider request, so the placeholder default
|
|
17
|
+
* (PI_DEFAULT_MAX_TOKENS) silently caps longer outputs. Override here, or
|
|
18
|
+
* per config via `requestParams.max_tokens`.
|
|
19
|
+
*/
|
|
20
|
+
readonly maxTokens?: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Placeholder model capabilities: the resolved config has no metadata to
|
|
24
|
+
* derive them from, so these defaults assume a large-context model. Callers
|
|
25
|
+
* with different models override via PiSessionEngineOptions (or
|
|
26
|
+
* `requestParams.max_tokens` for the output cap alone).
|
|
27
|
+
*/
|
|
28
|
+
export declare const PI_DEFAULT_CONTEXT_WINDOW = 128000;
|
|
29
|
+
export declare const PI_DEFAULT_MAX_TOKENS = 16384;
|
|
30
|
+
export declare function piSessionEngine(options: PiSessionEngineOptions): OpenAiCompatibleEngine;
|
|
31
|
+
export declare function piNodeVersionDoctorCheck(version: string): DoctorCheck;
|
|
32
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../../src/agents/openai-compatible/pi/engine.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAGrB,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,SAAU,CAAC;AACjD,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAQ5C,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,sBAAsB,CAExB;AA4ED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CASrE"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { AGENT_ERROR_CODE, isAgentError } from "../../../core/error.js";
|
|
2
|
+
import { DOCTOR_CHECK_STATUS, } from "../../../host/capabilities.js";
|
|
3
|
+
import { defaultPiModuleLoader, loadPiModule, } from "./module.js";
|
|
4
|
+
import { runPiSession } from "./session.js";
|
|
5
|
+
/**
|
|
6
|
+
* Placeholder model capabilities: the resolved config has no metadata to
|
|
7
|
+
* derive them from, so these defaults assume a large-context model. Callers
|
|
8
|
+
* with different models override via PiSessionEngineOptions (or
|
|
9
|
+
* `requestParams.max_tokens` for the output cap alone).
|
|
10
|
+
*/
|
|
11
|
+
export const PI_DEFAULT_CONTEXT_WINDOW = 128_000;
|
|
12
|
+
export const PI_DEFAULT_MAX_TOKENS = 16_384;
|
|
13
|
+
const MINIMUM_PI_NODE_VERSION = [22, 19, 0];
|
|
14
|
+
const DOCTOR_CHECK = {
|
|
15
|
+
node: "pi:node",
|
|
16
|
+
peer: "pi:peer",
|
|
17
|
+
};
|
|
18
|
+
export function piSessionEngine(options) {
|
|
19
|
+
return new PiSessionEngine(options);
|
|
20
|
+
}
|
|
21
|
+
class PiSessionEngine {
|
|
22
|
+
agentDir;
|
|
23
|
+
moduleLoader;
|
|
24
|
+
contextWindow;
|
|
25
|
+
maxTokens;
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.agentDir = options.agentDir;
|
|
28
|
+
this.moduleLoader = options.module ?? defaultPiModuleLoader;
|
|
29
|
+
this.contextWindow = options.contextWindow ?? PI_DEFAULT_CONTEXT_WINDOW;
|
|
30
|
+
this.maxTokens = options.maxTokens ?? PI_DEFAULT_MAX_TOKENS;
|
|
31
|
+
}
|
|
32
|
+
async doctorChecks() {
|
|
33
|
+
return [
|
|
34
|
+
piNodeVersionDoctorCheck(currentNodeVersion()),
|
|
35
|
+
await this.peerDoctorCheck(),
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
run(config, options, ctx) {
|
|
39
|
+
return this.runLoaded(config, options, ctx);
|
|
40
|
+
}
|
|
41
|
+
async *runLoaded(config, options, ctx) {
|
|
42
|
+
const module = await loadPiModule(this.moduleLoader);
|
|
43
|
+
return yield* runPiSession({
|
|
44
|
+
module,
|
|
45
|
+
agentDir: this.agentDir,
|
|
46
|
+
contextWindow: this.contextWindow,
|
|
47
|
+
maxTokens: this.maxTokens,
|
|
48
|
+
config,
|
|
49
|
+
options,
|
|
50
|
+
ctx,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async peerDoctorCheck() {
|
|
54
|
+
try {
|
|
55
|
+
await loadPiModule(this.moduleLoader);
|
|
56
|
+
return {
|
|
57
|
+
id: DOCTOR_CHECK.peer,
|
|
58
|
+
status: DOCTOR_CHECK_STATUS.pass,
|
|
59
|
+
detail: null,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (isAgentError(error, AGENT_ERROR_CODE.notAvailable)) {
|
|
64
|
+
return {
|
|
65
|
+
id: DOCTOR_CHECK.peer,
|
|
66
|
+
status: DOCTOR_CHECK_STATUS.fail,
|
|
67
|
+
detail: error.message,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
id: DOCTOR_CHECK.peer,
|
|
72
|
+
status: DOCTOR_CHECK_STATUS.fail,
|
|
73
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function currentNodeVersion() {
|
|
79
|
+
return process.versions.node;
|
|
80
|
+
}
|
|
81
|
+
export function piNodeVersionDoctorCheck(version) {
|
|
82
|
+
const pass = isVersionAtLeast(version, MINIMUM_PI_NODE_VERSION);
|
|
83
|
+
return {
|
|
84
|
+
id: DOCTOR_CHECK.node,
|
|
85
|
+
status: pass ? DOCTOR_CHECK_STATUS.pass : DOCTOR_CHECK_STATUS.fail,
|
|
86
|
+
detail: pass
|
|
87
|
+
? null
|
|
88
|
+
: `Pi session engine requires Node.js ${MINIMUM_PI_NODE_VERSION.join(".")} or newer`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function isVersionAtLeast(version, minimum) {
|
|
92
|
+
const [major = 0, minor = 0, patch = 0] = version
|
|
93
|
+
.split(".")
|
|
94
|
+
.map((segment) => Number(segment));
|
|
95
|
+
return (major > minimum[0] ||
|
|
96
|
+
(major === minimum[0] &&
|
|
97
|
+
(minor > minimum[1] || (minor === minimum[1] && patch >= minimum[2]))));
|
|
98
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { AgentError } from "../../../core/error.js";
|
|
2
|
+
export interface PiAuthStorage {
|
|
3
|
+
setRuntimeApiKey(provider: string, apiKey: string): void;
|
|
4
|
+
}
|
|
5
|
+
export interface PiModelRegistry {
|
|
6
|
+
registerProvider(providerName: string, config: unknown): void;
|
|
7
|
+
find(provider: string, modelId: string): unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface PiResourceLoader {
|
|
10
|
+
reload(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export interface PiPayloadHookSession {
|
|
13
|
+
agent?: {
|
|
14
|
+
onPayload?: (payload: unknown, model: unknown) => unknown | Promise<unknown>;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface PiAgentSession extends PiPayloadHookSession {
|
|
18
|
+
bindExtensions(bindings: object): Promise<void>;
|
|
19
|
+
prompt(text: string): Promise<void>;
|
|
20
|
+
subscribe?(listener: (event: unknown) => void): () => void;
|
|
21
|
+
abort?(): Promise<void>;
|
|
22
|
+
dispose(): void;
|
|
23
|
+
state?: {
|
|
24
|
+
messages?: readonly unknown[];
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export interface PiBashOperations {
|
|
28
|
+
exec(command: string, cwd: string, options: {
|
|
29
|
+
onData(data: Uint8Array): void;
|
|
30
|
+
signal?: AbortSignal;
|
|
31
|
+
timeout?: number;
|
|
32
|
+
env?: Record<string, string>;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
exitCode: number | null;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
export type PiBashToolDefinitionFactory = (cwd: string, options: {
|
|
38
|
+
operations: PiBashOperations;
|
|
39
|
+
}) => unknown;
|
|
40
|
+
export type PiLocalBashOperationsFactory = () => PiBashOperations;
|
|
41
|
+
export declare const PI_NO_TOOLS_MODE: {
|
|
42
|
+
readonly all: "all";
|
|
43
|
+
readonly builtin: "builtin";
|
|
44
|
+
};
|
|
45
|
+
export type PiNoToolsMode = (typeof PI_NO_TOOLS_MODE)[keyof typeof PI_NO_TOOLS_MODE];
|
|
46
|
+
export interface PiModule {
|
|
47
|
+
AuthStorage: {
|
|
48
|
+
inMemory(data?: unknown): PiAuthStorage;
|
|
49
|
+
};
|
|
50
|
+
ModelRegistry: {
|
|
51
|
+
inMemory(authStorage: PiAuthStorage): PiModelRegistry;
|
|
52
|
+
};
|
|
53
|
+
DefaultResourceLoader: new (options: {
|
|
54
|
+
cwd: string;
|
|
55
|
+
agentDir: string;
|
|
56
|
+
settingsManager?: unknown;
|
|
57
|
+
additionalSkillPaths?: readonly string[];
|
|
58
|
+
}) => PiResourceLoader;
|
|
59
|
+
SessionManager: {
|
|
60
|
+
inMemory(cwd?: string): unknown;
|
|
61
|
+
};
|
|
62
|
+
SettingsManager: {
|
|
63
|
+
inMemory(settings?: unknown): unknown;
|
|
64
|
+
};
|
|
65
|
+
createAgentSession(options: {
|
|
66
|
+
cwd: string;
|
|
67
|
+
agentDir: string;
|
|
68
|
+
authStorage: PiAuthStorage;
|
|
69
|
+
modelRegistry: PiModelRegistry;
|
|
70
|
+
model: unknown;
|
|
71
|
+
resourceLoader: PiResourceLoader;
|
|
72
|
+
sessionManager: unknown;
|
|
73
|
+
settingsManager: unknown;
|
|
74
|
+
tools?: readonly string[];
|
|
75
|
+
noTools?: PiNoToolsMode;
|
|
76
|
+
customTools?: readonly unknown[];
|
|
77
|
+
}): Promise<{
|
|
78
|
+
session: PiAgentSession;
|
|
79
|
+
}>;
|
|
80
|
+
createBashToolDefinition?: PiBashToolDefinitionFactory;
|
|
81
|
+
createLocalBashOperations?: PiLocalBashOperationsFactory;
|
|
82
|
+
}
|
|
83
|
+
export type PiModuleLoader = () => Promise<unknown>;
|
|
84
|
+
export declare const defaultPiModuleLoader: PiModuleLoader;
|
|
85
|
+
export declare function loadPiModule(loader: PiModuleLoader): Promise<PiModule>;
|
|
86
|
+
export declare function piPeerMissingError(cause: unknown): AgentError;
|
|
87
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../src/agents/openai-compatible/pi/module.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// biome-ignore-all lint/style/useNamingConvention: Pi peer exports mirror upstream PascalCase names.
|
|
2
|
+
import { AGENT_ERROR_CODE, AgentError, NOT_AVAILABLE_CAUSE, } from "../../../core/error.js";
|
|
3
|
+
export const PI_NO_TOOLS_MODE = {
|
|
4
|
+
all: "all",
|
|
5
|
+
builtin: "builtin",
|
|
6
|
+
};
|
|
7
|
+
const PI_PEER_PACKAGE = "@earendil-works/pi-coding-agent";
|
|
8
|
+
const PI_PEER_MISSING_MESSAGE = "Pi session engine peer is not available";
|
|
9
|
+
const REQUIRED_EXPORTS = [
|
|
10
|
+
"AuthStorage",
|
|
11
|
+
"ModelRegistry",
|
|
12
|
+
"DefaultResourceLoader",
|
|
13
|
+
"SessionManager",
|
|
14
|
+
"SettingsManager",
|
|
15
|
+
"createAgentSession",
|
|
16
|
+
];
|
|
17
|
+
export const defaultPiModuleLoader = () => import(PI_PEER_PACKAGE);
|
|
18
|
+
export async function loadPiModule(loader) {
|
|
19
|
+
let loaded;
|
|
20
|
+
try {
|
|
21
|
+
loaded = await loader();
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw piPeerMissingError(error);
|
|
25
|
+
}
|
|
26
|
+
if (typeof loaded !== "object" || loaded === null) {
|
|
27
|
+
throw piPeerMissingError(null);
|
|
28
|
+
}
|
|
29
|
+
for (const key of REQUIRED_EXPORTS) {
|
|
30
|
+
if (Reflect.get(loaded, key) === undefined) {
|
|
31
|
+
throw piPeerMissingError(null);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return loaded;
|
|
35
|
+
}
|
|
36
|
+
export function piPeerMissingError(cause) {
|
|
37
|
+
return new AgentError({
|
|
38
|
+
code: AGENT_ERROR_CODE.notAvailable,
|
|
39
|
+
message: PI_PEER_MISSING_MESSAGE,
|
|
40
|
+
data: { cause: NOT_AVAILABLE_CAUSE.engineMissing },
|
|
41
|
+
cause,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Grant, type ResolvedSandbox, SANDBOX_MODE, type SandboxMode } from "../../../kernel/options.js";
|
|
2
|
+
import type { PiModule, PiNoToolsMode } from "./module.js";
|
|
3
|
+
export type PiToolPolicy = {
|
|
4
|
+
readonly tools?: readonly string[];
|
|
5
|
+
readonly noTools?: PiNoToolsMode;
|
|
6
|
+
readonly customTools?: readonly unknown[];
|
|
7
|
+
};
|
|
8
|
+
export type PiSandboxPolicyInput = {
|
|
9
|
+
readonly module: PiModule;
|
|
10
|
+
readonly workspace: string;
|
|
11
|
+
};
|
|
12
|
+
export type PiSandboxPolicyBuilder<M extends SandboxMode> = (sandbox: Extract<ResolvedSandbox, {
|
|
13
|
+
mode: M;
|
|
14
|
+
}>, input: PiSandboxPolicyInput) => PiToolPolicy;
|
|
15
|
+
export declare const piSandboxToolPolicies: {
|
|
16
|
+
full: (_sandbox: {
|
|
17
|
+
mode: typeof SANDBOX_MODE.full;
|
|
18
|
+
}, _input: PiSandboxPolicyInput) => {};
|
|
19
|
+
"read-only": (sandbox: {
|
|
20
|
+
mode: typeof SANDBOX_MODE.readOnly;
|
|
21
|
+
grants: Grant[];
|
|
22
|
+
}, input: PiSandboxPolicyInput) => PiToolPolicy;
|
|
23
|
+
};
|
|
24
|
+
export declare function piSandboxToolPolicy(sandbox: ResolvedSandbox, input: PiSandboxPolicyInput): PiToolPolicy;
|
|
25
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../../../src/agents/openai-compatible/pi/sandbox.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,KAAK,EACV,KAAK,eAAe,EACpB,YAAY,EACZ,KAAK,WAAW,EACjB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,QAAQ,EAAoB,aAAa,EAAE,MAAM,aAAa,CAAC;AAE7E,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,WAAW,IAAI,CAC1D,OAAO,EAAE,OAAO,CAAC,eAAe,EAAE;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,EAC9C,KAAK,EAAE,oBAAoB,KACxB,YAAY,CAAC;AAWlB,eAAO,MAAM,qBAAqB;;;;;;;;CAMjC,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,oBAAoB,GAC1B,YAAY,CASd"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { AGENT_ERROR_CODE, AgentError, NOT_AVAILABLE_CAUSE, } from "../../../core/error.js";
|
|
2
|
+
import { GRANT_KIND, SANDBOX_MODE, } from "../../../kernel/options.js";
|
|
3
|
+
const READ_ONLY_TOOLS = ["read", "grep", "find", "ls"];
|
|
4
|
+
const PI_BASH_TOOL = "bash";
|
|
5
|
+
const SHELL_COMMAND_SEPARATOR = " ";
|
|
6
|
+
const PI_SHELL_GRANT_MISSING_FACTORY_MESSAGE = "Pi shell grants require bash tool factory exports";
|
|
7
|
+
const PI_BASH_COMMAND_NOT_ALLOWED_MESSAGE = "Pi bash command is not allowed by sandbox grants";
|
|
8
|
+
const PI_BASH_COMMAND_NOT_ALLOWED_REASON = "pi bash command not allowed";
|
|
9
|
+
export const piSandboxToolPolicies = {
|
|
10
|
+
[SANDBOX_MODE.full]: (_sandbox, _input) => ({}),
|
|
11
|
+
[SANDBOX_MODE.readOnly]: (sandbox, input) => readOnlyToolPolicy(sandbox.grants, input),
|
|
12
|
+
};
|
|
13
|
+
export function piSandboxToolPolicy(sandbox, input) {
|
|
14
|
+
// Correlated-union dispatch: TS cannot connect the indexed record row to
|
|
15
|
+
// the narrowed union member, so the one cast lives here; the `satisfies`
|
|
16
|
+
// map keeps every row's narrow signature honest.
|
|
17
|
+
const builder = piSandboxToolPolicies[sandbox.mode];
|
|
18
|
+
return builder(sandbox, input);
|
|
19
|
+
}
|
|
20
|
+
function readOnlyToolPolicy(grants, input) {
|
|
21
|
+
const shellCommands = grants.flatMap((grant) => grant.kind === GRANT_KIND.shell ? [grant.command] : []);
|
|
22
|
+
const toolGrants = grants.flatMap((grant) => grant.kind === GRANT_KIND.tool ? [grant.name] : []);
|
|
23
|
+
const tools = [
|
|
24
|
+
...READ_ONLY_TOOLS,
|
|
25
|
+
...toolGrants,
|
|
26
|
+
...(shellCommands.length === 0 ? [] : [PI_BASH_TOOL]),
|
|
27
|
+
];
|
|
28
|
+
return {
|
|
29
|
+
tools: [...new Set(tools)],
|
|
30
|
+
...(shellCommands.length === 0
|
|
31
|
+
? {}
|
|
32
|
+
: { customTools: [createGuardedBashTool(input, shellCommands)] }),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function createGuardedBashTool(input, allowlist) {
|
|
36
|
+
const createDefinition = input.module.createBashToolDefinition;
|
|
37
|
+
const createOperations = input.module.createLocalBashOperations;
|
|
38
|
+
if (createDefinition === undefined || createOperations === undefined) {
|
|
39
|
+
throw new AgentError({
|
|
40
|
+
code: AGENT_ERROR_CODE.notAvailable,
|
|
41
|
+
message: PI_SHELL_GRANT_MISSING_FACTORY_MESSAGE,
|
|
42
|
+
data: { cause: NOT_AVAILABLE_CAUSE.engineMissing },
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const localOperations = createOperations();
|
|
46
|
+
return createDefinition(input.workspace, {
|
|
47
|
+
operations: guardedBashOperations(localOperations, allowlist),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function guardedBashOperations(operations, allowlist) {
|
|
51
|
+
return {
|
|
52
|
+
exec(command, cwd, options) {
|
|
53
|
+
assertAllowedCommand(command, allowlist);
|
|
54
|
+
return operations.exec(command, cwd, options);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function assertAllowedCommand(command, allowlist) {
|
|
59
|
+
const allowed = allowlist.some((entry) => command === entry ||
|
|
60
|
+
command.startsWith(`${entry}${SHELL_COMMAND_SEPARATOR}`));
|
|
61
|
+
if (allowed) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
throw new AgentError({
|
|
65
|
+
code: AGENT_ERROR_CODE.invalidInput,
|
|
66
|
+
message: PI_BASH_COMMAND_NOT_ALLOWED_MESSAGE,
|
|
67
|
+
data: { reason: PI_BASH_COMMAND_NOT_ALLOWED_REASON },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AgentRun, RunContext } from "../../../kernel/definition.js";
|
|
2
|
+
import type { ResolvedRunOptions } from "../../../kernel/options.js";
|
|
3
|
+
import type { ResolvedOpenAiCompatibleConfig } from "../types.js";
|
|
4
|
+
import type { PiModule } from "./module.js";
|
|
5
|
+
type PiSessionRunInput = {
|
|
6
|
+
readonly module: PiModule;
|
|
7
|
+
readonly agentDir: string;
|
|
8
|
+
/** Resolved by the engine (option or placeholder default). */
|
|
9
|
+
readonly contextWindow: number;
|
|
10
|
+
readonly maxTokens: number;
|
|
11
|
+
readonly config: ResolvedOpenAiCompatibleConfig;
|
|
12
|
+
readonly options: ResolvedRunOptions;
|
|
13
|
+
readonly ctx: RunContext;
|
|
14
|
+
};
|
|
15
|
+
export declare function runPiSession(input: PiSessionRunInput): AgentRun;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../../src/agents/openai-compatible/pi/session.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAkB,QAAQ,EAAE,MAAM,aAAa,CAAC;AA8E5D,KAAK,iBAAiB,GAAG;IACvB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,8BAA8B,CAAC;IAChD,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;CAC1B,CAAC;AAWF,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,QAAQ,CAE/D"}
|