@action-llama/action-llama 0.27.5 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/credential-setup.d.ts +0 -1
- package/dist/agents/credential-setup.d.ts.map +1 -1
- package/dist/agents/credential-setup.js +2 -23
- package/dist/agents/credential-setup.js.map +1 -1
- package/dist/agents/prompt.js +1 -1
- package/dist/agents/prompt.js.map +1 -1
- package/dist/agents/scheduler-tools.d.ts +40 -0
- package/dist/agents/scheduler-tools.d.ts.map +1 -0
- package/dist/agents/scheduler-tools.js +178 -0
- package/dist/agents/scheduler-tools.js.map +1 -0
- package/dist/agents/transport-runner.d.ts +95 -0
- package/dist/agents/transport-runner.d.ts.map +1 -0
- package/dist/agents/transport-runner.js +653 -0
- package/dist/agents/transport-runner.js.map +1 -0
- package/dist/build-info.json +1 -1
- package/dist/cli/commands/add.d.ts +1 -0
- package/dist/cli/commands/add.d.ts.map +1 -1
- package/dist/cli/commands/add.js +24 -9
- package/dist/cli/commands/add.js.map +1 -1
- package/dist/cli/commands/agent.d.ts +0 -3
- package/dist/cli/commands/agent.d.ts.map +1 -1
- package/dist/cli/commands/agent.js +3 -67
- package/dist/cli/commands/agent.js.map +1 -1
- package/dist/cli/main.js +1 -30
- package/dist/cli/main.js.map +1 -1
- package/dist/control/routes/dashboard-api.js +1 -1
- package/dist/control/routes/dashboard-api.js.map +1 -1
- package/dist/control/routes/log-helpers.d.ts +4 -4
- package/dist/control/routes/log-helpers.d.ts.map +1 -1
- package/dist/control/routes/log-helpers.js +12 -7
- package/dist/control/routes/log-helpers.js.map +1 -1
- package/dist/control/routes/logs.d.ts.map +1 -1
- package/dist/control/routes/logs.js +10 -10
- package/dist/control/routes/logs.js.map +1 -1
- package/dist/docker/providers/index.d.ts +0 -4
- package/dist/docker/providers/index.d.ts.map +1 -1
- package/dist/docker/providers/index.js +0 -38
- package/dist/docker/providers/index.js.map +1 -1
- package/dist/execution/execution.d.ts +0 -1
- package/dist/execution/execution.d.ts.map +1 -1
- package/dist/execution/execution.js +5 -9
- package/dist/execution/execution.js.map +1 -1
- package/dist/execution/index.d.ts +1 -11
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +1 -8
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/runner-setup.d.ts +6 -11
- package/dist/execution/runner-setup.d.ts.map +1 -1
- package/dist/execution/runner-setup.js +18 -14
- package/dist/execution/runner-setup.js.map +1 -1
- package/dist/execution/runtime-factory.d.ts +1 -15
- package/dist/execution/runtime-factory.d.ts.map +1 -1
- package/dist/execution/runtime-factory.js +1 -18
- package/dist/execution/runtime-factory.js.map +1 -1
- package/dist/gateway/index.d.ts +1 -1
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js +6 -47
- package/dist/gateway/index.js.map +1 -1
- package/dist/gateway/routes/system.d.ts +1 -4
- package/dist/gateway/routes/system.d.ts.map +1 -1
- package/dist/gateway/routes/system.js +3 -8
- package/dist/gateway/routes/system.js.map +1 -1
- package/dist/gateway/stores.d.ts +0 -4
- package/dist/gateway/stores.d.ts.map +1 -1
- package/dist/gateway/stores.js +2 -10
- package/dist/gateway/stores.js.map +1 -1
- package/dist/gateway/types.d.ts +0 -13
- package/dist/gateway/types.d.ts.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +11 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/scheduler/gateway-setup.d.ts +0 -2
- package/dist/scheduler/gateway-setup.d.ts.map +1 -1
- package/dist/scheduler/gateway-setup.js +2 -11
- package/dist/scheduler/gateway-setup.js.map +1 -1
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +95 -55
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/validation.js +1 -1
- package/dist/scheduler/validation.js.map +1 -1
- package/dist/scheduler/watcher.d.ts +2 -8
- package/dist/scheduler/watcher.d.ts.map +1 -1
- package/dist/scheduler/watcher.js +7 -104
- package/dist/scheduler/watcher.js.map +1 -1
- package/dist/shared/config/load-agent.js +2 -2
- package/dist/shared/config/load-agent.js.map +1 -1
- package/dist/shared/config/load-project.js +2 -2
- package/dist/shared/config/load-project.js.map +1 -1
- package/dist/shared/config/types.d.ts +10 -2
- package/dist/shared/config/types.d.ts.map +1 -1
- package/dist/shared/constants.d.ts +1 -1
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +2 -2
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/credential-refs.js +1 -1
- package/dist/shared/credential-refs.js.map +1 -1
- package/dist/shared/paths.d.ts.map +1 -1
- package/dist/shared/paths.js +2 -2
- package/dist/shared/paths.js.map +1 -1
- package/dist/shared/validation.js +1 -1
- package/dist/shared/validation.js.map +1 -1
- package/dist/transport/docker-exec.d.ts +41 -0
- package/dist/transport/docker-exec.d.ts.map +1 -0
- package/dist/transport/docker-exec.js +331 -0
- package/dist/transport/docker-exec.js.map +1 -0
- package/dist/transport/host-user.d.ts +37 -0
- package/dist/transport/host-user.d.ts.map +1 -0
- package/dist/transport/host-user.js +232 -0
- package/dist/transport/host-user.js.map +1 -0
- package/dist/transport/index.d.ts +8 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +7 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/memory.d.ts +35 -0
- package/dist/transport/memory.d.ts.map +1 -0
- package/dist/transport/memory.js +110 -0
- package/dist/transport/memory.js.map +1 -0
- package/dist/transport/operations.d.ts +68 -0
- package/dist/transport/operations.d.ts.map +1 -0
- package/dist/transport/operations.js +164 -0
- package/dist/transport/operations.js.map +1 -0
- package/dist/transport/ssh.d.ts +43 -0
- package/dist/transport/ssh.d.ts.map +1 -0
- package/dist/transport/ssh.js +225 -0
- package/dist/transport/ssh.js.map +1 -0
- package/dist/transport/transport.d.ts +59 -0
- package/dist/transport/transport.d.ts.map +1 -0
- package/dist/transport/transport.js +29 -0
- package/dist/transport/transport.js.map +1 -0
- package/package.json +1 -1
- package/dist/agents/container-entry.d.ts +0 -31
- package/dist/agents/container-entry.d.ts.map +0 -1
- package/dist/agents/container-entry.js +0 -302
- package/dist/agents/container-entry.js.map +0 -1
- package/dist/agents/container-runner.d.ts +0 -59
- package/dist/agents/container-runner.d.ts.map +0 -1
- package/dist/agents/container-runner.js +0 -472
- package/dist/agents/container-runner.js.map +0 -1
- package/dist/agents/harness/claude-cli-harness.d.ts +0 -15
- package/dist/agents/harness/claude-cli-harness.d.ts.map +0 -1
- package/dist/agents/harness/claude-cli-harness.js +0 -260
- package/dist/agents/harness/claude-cli-harness.js.map +0 -1
- package/dist/agents/harness/consumer.d.ts +0 -31
- package/dist/agents/harness/consumer.d.ts.map +0 -1
- package/dist/agents/harness/consumer.js +0 -165
- package/dist/agents/harness/consumer.js.map +0 -1
- package/dist/agents/harness/factory.d.ts +0 -9
- package/dist/agents/harness/factory.d.ts.map +0 -1
- package/dist/agents/harness/factory.js +0 -25
- package/dist/agents/harness/factory.js.map +0 -1
- package/dist/agents/harness/index.d.ts +0 -9
- package/dist/agents/harness/index.d.ts.map +0 -1
- package/dist/agents/harness/index.js +0 -5
- package/dist/agents/harness/index.js.map +0 -1
- package/dist/agents/harness/pi-harness.d.ts +0 -18
- package/dist/agents/harness/pi-harness.d.ts.map +0 -1
- package/dist/agents/harness/pi-harness.js +0 -278
- package/dist/agents/harness/pi-harness.js.map +0 -1
- package/dist/agents/harness/types.d.ts +0 -57
- package/dist/agents/harness/types.d.ts.map +0 -1
- package/dist/agents/harness/types.js +0 -2
- package/dist/agents/harness/types.js.map +0 -1
- package/dist/agents/session-loop.d.ts +0 -36
- package/dist/agents/session-loop.d.ts.map +0 -1
- package/dist/agents/session-loop.js +0 -216
- package/dist/agents/session-loop.js.map +0 -1
- package/dist/agents/signals.d.ts +0 -34
- package/dist/agents/signals.d.ts.map +0 -1
- package/dist/agents/signals.js +0 -122
- package/dist/agents/signals.js.map +0 -1
- package/dist/cli/commands/claude.d.ts +0 -4
- package/dist/cli/commands/claude.d.ts.map +0 -1
- package/dist/cli/commands/claude.js +0 -6
- package/dist/cli/commands/claude.js.map +0 -1
- package/dist/cli/commands/run-agent.d.ts +0 -14
- package/dist/cli/commands/run-agent.d.ts.map +0 -1
- package/dist/cli/commands/run-agent.js +0 -270
- package/dist/cli/commands/run-agent.js.map +0 -1
- package/dist/docker/cloud-run-runtime.d.ts +0 -48
- package/dist/docker/cloud-run-runtime.d.ts.map +0 -1
- package/dist/docker/cloud-run-runtime.js +0 -490
- package/dist/docker/cloud-run-runtime.js.map +0 -1
- package/dist/docker/image.d.ts +0 -19
- package/dist/docker/image.d.ts.map +0 -1
- package/dist/docker/image.js +0 -111
- package/dist/docker/image.js.map +0 -1
- package/dist/execution/call-dispatcher.d.ts +0 -11
- package/dist/execution/call-dispatcher.d.ts.map +0 -1
- package/dist/execution/call-dispatcher.js +0 -75
- package/dist/execution/call-dispatcher.js.map +0 -1
- package/dist/execution/container-registry.d.ts +0 -42
- package/dist/execution/container-registry.d.ts.map +0 -1
- package/dist/execution/container-registry.js +0 -76
- package/dist/execution/container-registry.js.map +0 -1
- package/dist/execution/image-builder.d.ts +0 -48
- package/dist/execution/image-builder.d.ts.map +0 -1
- package/dist/execution/image-builder.js +0 -155
- package/dist/execution/image-builder.js.map +0 -1
- package/dist/execution/routes/calls.d.ts +0 -18
- package/dist/execution/routes/calls.d.ts.map +0 -1
- package/dist/execution/routes/calls.js +0 -74
- package/dist/execution/routes/calls.js.map +0 -1
- package/dist/execution/routes/locks.d.ts +0 -10
- package/dist/execution/routes/locks.d.ts.map +0 -1
- package/dist/execution/routes/locks.js +0 -166
- package/dist/execution/routes/locks.js.map +0 -1
- package/dist/execution/routes/shutdown.d.ts +0 -5
- package/dist/execution/routes/shutdown.d.ts.map +0 -1
- package/dist/execution/routes/shutdown.js +0 -24
- package/dist/execution/routes/shutdown.js.map +0 -1
- package/dist/execution/routes/signals.d.ts +0 -12
- package/dist/execution/routes/signals.d.ts.map +0 -1
- package/dist/execution/routes/signals.js +0 -123
- package/dist/execution/routes/signals.js.map +0 -1
- package/dist/execution/types.d.ts +0 -23
- package/dist/execution/types.d.ts.map +0 -1
- package/dist/execution/types.js +0 -2
- package/dist/execution/types.js.map +0 -1
- package/dist/gateway/routes/execution.d.ts +0 -24
- package/dist/gateway/routes/execution.d.ts.map +0 -1
- package/dist/gateway/routes/execution.js +0 -13
- package/dist/gateway/routes/execution.js.map +0 -1
- package/dist/scheduler/orphan-recovery.d.ts +0 -25
- package/dist/scheduler/orphan-recovery.d.ts.map +0 -1
- package/dist/scheduler/orphan-recovery.js +0 -144
- package/dist/scheduler/orphan-recovery.js.map +0 -1
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TransportAgentRunner — runs Pi sessions in the scheduler process,
|
|
3
|
+
* driving a remote runtime via a Transport.
|
|
4
|
+
*
|
|
5
|
+
* Replaces ContainerAgentRunner for the centralized architecture where:
|
|
6
|
+
* - The scheduler is the "brain" (LLM session, tool orchestration)
|
|
7
|
+
* - The runtime is the "body" (filesystem, shell, credentials)
|
|
8
|
+
* - The transport connects them (DockerExecTransport, SshTransport, etc.)
|
|
9
|
+
*
|
|
10
|
+
* Key differences from ContainerAgentRunner:
|
|
11
|
+
* - Pi session runs in-process (not inside the container)
|
|
12
|
+
* - Events are captured directly (no JSON log parsing)
|
|
13
|
+
* - Credentials staged via transport (no volume mounts needed)
|
|
14
|
+
* - No gateway registration (direct access to scheduler services)
|
|
15
|
+
* - No signal files (scheduler tools handle reruns, returns, etc.)
|
|
16
|
+
*/
|
|
17
|
+
import { randomBytes } from "crypto";
|
|
18
|
+
import { execFileSync } from "child_process";
|
|
19
|
+
import { getModel } from "@mariozechner/pi-ai";
|
|
20
|
+
import { AuthStorage, createAgentSession, SessionManager, SettingsManager, DefaultResourceLoader, ModelRegistry, } from "@mariozechner/pi-coding-agent";
|
|
21
|
+
import { sessionStatsToUsage } from "../shared/usage.js";
|
|
22
|
+
import { DEFAULT_AGENT_TIMEOUT } from "../shared/constants.js";
|
|
23
|
+
import { selectAvailableModels, isRateLimitError } from "./model-fallback.js";
|
|
24
|
+
import { DockerExecTransport } from "../transport/docker-exec.js";
|
|
25
|
+
import { SshTransport } from "../transport/ssh.js";
|
|
26
|
+
import { HostUserTransport } from "../transport/host-user.js";
|
|
27
|
+
import { createTransportTools } from "../transport/operations.js";
|
|
28
|
+
import { parseCredentialRef, getDefaultBackend } from "../shared/credentials.js";
|
|
29
|
+
import { parseFrontmatter } from "../shared/frontmatter.js";
|
|
30
|
+
import { withSpan } from "../telemetry/index.js";
|
|
31
|
+
import { SpanKind } from "@opentelemetry/api";
|
|
32
|
+
import { createSchedulerTools } from "./scheduler-tools.js";
|
|
33
|
+
const MAX_MODEL_PASSES = 3;
|
|
34
|
+
const DEFAULT_BACKOFF_MS = 30_000;
|
|
35
|
+
const MAX_BACKOFF_MS = 300_000;
|
|
36
|
+
const CONTAINER_CWD = "/workspace";
|
|
37
|
+
export class TransportAgentRunner {
|
|
38
|
+
_running = false;
|
|
39
|
+
_aborting = false;
|
|
40
|
+
_transport = null;
|
|
41
|
+
_containerName = null;
|
|
42
|
+
_session = null;
|
|
43
|
+
instanceId;
|
|
44
|
+
/** Trigger depth for subagent call tracking. Set by the scheduler before run(). */
|
|
45
|
+
depth = 0;
|
|
46
|
+
globalConfig;
|
|
47
|
+
agentConfig;
|
|
48
|
+
baseLogger;
|
|
49
|
+
logger;
|
|
50
|
+
circuitBreaker;
|
|
51
|
+
statusTracker;
|
|
52
|
+
baseImage;
|
|
53
|
+
projectPath;
|
|
54
|
+
providerKeys;
|
|
55
|
+
schedulerToolsDeps;
|
|
56
|
+
constructor(opts) {
|
|
57
|
+
this.globalConfig = opts.globalConfig;
|
|
58
|
+
this.agentConfig = opts.agentConfig;
|
|
59
|
+
this.baseLogger = opts.logger;
|
|
60
|
+
this.logger = opts.logger;
|
|
61
|
+
this.circuitBreaker = opts.circuitBreaker;
|
|
62
|
+
this.statusTracker = opts.statusTracker;
|
|
63
|
+
this.baseImage = opts.baseImage;
|
|
64
|
+
this.projectPath = opts.projectPath;
|
|
65
|
+
this.providerKeys = opts.providerKeys ?? new Map();
|
|
66
|
+
this.schedulerToolsDeps = opts.schedulerToolsDeps;
|
|
67
|
+
this.instanceId = opts.agentConfig.name;
|
|
68
|
+
}
|
|
69
|
+
get isRunning() {
|
|
70
|
+
return this._running;
|
|
71
|
+
}
|
|
72
|
+
setAgentConfig(config) {
|
|
73
|
+
this.agentConfig = config;
|
|
74
|
+
}
|
|
75
|
+
abort() {
|
|
76
|
+
this._aborting = true;
|
|
77
|
+
this.logger.info("Transport agent runner abort requested");
|
|
78
|
+
// Dispose the Pi session to stop the LLM loop
|
|
79
|
+
if (this._session) {
|
|
80
|
+
try {
|
|
81
|
+
this._session.dispose();
|
|
82
|
+
}
|
|
83
|
+
catch { /* best effort */ }
|
|
84
|
+
}
|
|
85
|
+
// Kill the container (only applies to container runtime)
|
|
86
|
+
if (this._containerName) {
|
|
87
|
+
try {
|
|
88
|
+
execFileSync("docker", ["kill", this._containerName], {
|
|
89
|
+
timeout: 10_000,
|
|
90
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch { /* container may already be dead */ }
|
|
94
|
+
}
|
|
95
|
+
// Close the transport
|
|
96
|
+
if (this._transport) {
|
|
97
|
+
this._transport.close().catch(() => { });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async run(prompt, triggerInfo, instanceId) {
|
|
101
|
+
if (this._running) {
|
|
102
|
+
this.logger.warn(`${this.agentConfig.name} is already running, skipping`);
|
|
103
|
+
return { result: "error", triggers: [] };
|
|
104
|
+
}
|
|
105
|
+
this._running = true;
|
|
106
|
+
this._aborting = false;
|
|
107
|
+
this.instanceId = instanceId ?? `${this.agentConfig.name}-${randomBytes(4).toString("hex")}`;
|
|
108
|
+
this.logger = this.baseLogger.child({ instance: this.instanceId });
|
|
109
|
+
try {
|
|
110
|
+
return await withSpan("transport_agent.run", async (span) => {
|
|
111
|
+
span.setAttributes({
|
|
112
|
+
"agent.name": this.agentConfig.name,
|
|
113
|
+
"agent.run_id": this.instanceId,
|
|
114
|
+
"agent.trigger_type": triggerInfo?.type || "manual",
|
|
115
|
+
"agent.trigger_source": triggerInfo?.source || "",
|
|
116
|
+
"agent.model_provider": this.agentConfig.models[0]?.provider,
|
|
117
|
+
"agent.model_name": this.agentConfig.models[0]?.model,
|
|
118
|
+
"execution.environment": "transport",
|
|
119
|
+
});
|
|
120
|
+
return this.runInternal(prompt, triggerInfo, span);
|
|
121
|
+
}, {}, SpanKind.INTERNAL);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
this._running = false;
|
|
125
|
+
this.logger.error({ err }, "transport run setup failed");
|
|
126
|
+
return { result: "error", triggers: [] };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async runInternal(prompt, triggerInfo, parentSpan) {
|
|
130
|
+
const runStartTime = Date.now();
|
|
131
|
+
let runResult = "error";
|
|
132
|
+
let runError;
|
|
133
|
+
let returnValue;
|
|
134
|
+
let tokenUsage;
|
|
135
|
+
// Surface run start in TUI
|
|
136
|
+
const runReason = triggerInfo
|
|
137
|
+
? (triggerInfo.source
|
|
138
|
+
? (triggerInfo.type === 'agent' ? `triggered by ${triggerInfo.source}` : `${triggerInfo.type} (${triggerInfo.source})`)
|
|
139
|
+
: triggerInfo.type)
|
|
140
|
+
: undefined;
|
|
141
|
+
this.statusTracker?.startRun(this.agentConfig.name, runReason);
|
|
142
|
+
this.statusTracker?.registerInstance({
|
|
143
|
+
id: this.instanceId,
|
|
144
|
+
agentName: this.agentConfig.name,
|
|
145
|
+
status: "running",
|
|
146
|
+
startedAt: new Date(),
|
|
147
|
+
trigger: triggerInfo?.source ? `${triggerInfo.type}:${triggerInfo.source}` : (triggerInfo?.type ?? "manual"),
|
|
148
|
+
});
|
|
149
|
+
this.logger.info(`Starting ${this.agentConfig.name} transport run`);
|
|
150
|
+
this.statusTracker?.addLogLine(this.agentConfig.name, `${this.instanceId} started (${runReason ?? "manual"})`);
|
|
151
|
+
// ── Timeout — kill the entire run if it exceeds the configured limit ──
|
|
152
|
+
const timeoutSeconds = this.agentConfig.timeout ?? this.globalConfig.local?.timeout ?? DEFAULT_AGENT_TIMEOUT;
|
|
153
|
+
const timeoutTimer = setTimeout(() => {
|
|
154
|
+
this.logger.error({ timeoutSeconds }, "agent timeout reached, aborting");
|
|
155
|
+
this.abort();
|
|
156
|
+
}, timeoutSeconds * 1000);
|
|
157
|
+
timeoutTimer.unref();
|
|
158
|
+
try {
|
|
159
|
+
// ── 1–2. Provision runtime & connect transport ──────────
|
|
160
|
+
const transport = await this.createTransport();
|
|
161
|
+
this._transport = transport;
|
|
162
|
+
// ── 3. Stage credentials ─────────────────────────────────
|
|
163
|
+
const providerKeys = await this.stageCredentials(transport);
|
|
164
|
+
// Merge with pre-configured provider keys
|
|
165
|
+
for (const [k, v] of this.providerKeys) {
|
|
166
|
+
if (!providerKeys.has(k))
|
|
167
|
+
providerKeys.set(k, v);
|
|
168
|
+
}
|
|
169
|
+
// ── 4. Run hooks.pre ─────────────────────────────────────
|
|
170
|
+
if (this.agentConfig.hooks?.pre && this.agentConfig.hooks.pre.length > 0) {
|
|
171
|
+
for (const hook of this.agentConfig.hooks.pre) {
|
|
172
|
+
this.logger.info({ hook }, "running pre hook via transport");
|
|
173
|
+
await transport.exec(hook);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// ── 5. Build SKILL.md and prompt ─────────────────────────
|
|
177
|
+
// Read SKILL.md from the project directory on the host
|
|
178
|
+
const { readFileSync, existsSync } = await import("fs");
|
|
179
|
+
const { join } = await import("path");
|
|
180
|
+
const skillPath = join(this.projectPath, "agents", this.agentConfig.name, "SKILL.md");
|
|
181
|
+
let skillBody = "";
|
|
182
|
+
if (existsSync(skillPath)) {
|
|
183
|
+
const { body } = parseFrontmatter(readFileSync(skillPath, "utf-8"));
|
|
184
|
+
skillBody = body;
|
|
185
|
+
}
|
|
186
|
+
// Process context injection (runs commands on the transport)
|
|
187
|
+
// TODO: In the future, context injection should execute on the transport
|
|
188
|
+
// For now, we skip it since the commands won't have access to the runtime
|
|
189
|
+
// ── 6. Create Pi session with transport tools ────────────
|
|
190
|
+
const result = await this.runPiSession(prompt, skillBody, transport, providerKeys);
|
|
191
|
+
runResult = result.result;
|
|
192
|
+
returnValue = result.returnValue;
|
|
193
|
+
tokenUsage = result.usage;
|
|
194
|
+
runError = result.error;
|
|
195
|
+
// ── 7. Run hooks.post ────────────────────────────────────
|
|
196
|
+
if (this.agentConfig.hooks?.post && this.agentConfig.hooks.post.length > 0) {
|
|
197
|
+
for (const hook of this.agentConfig.hooks.post) {
|
|
198
|
+
this.logger.info({ hook }, "running post hook via transport");
|
|
199
|
+
try {
|
|
200
|
+
await transport.exec(hook);
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
this.logger.error({ err }, "post hook failed");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
this.logger.error({ err }, `${this.agentConfig.name} transport run failed`);
|
|
210
|
+
runError = String(err?.message || err).slice(0, 200);
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
clearTimeout(timeoutTimer);
|
|
214
|
+
// ── 8. Cleanup ───────────────────────────────────────────
|
|
215
|
+
if (this._transport) {
|
|
216
|
+
try {
|
|
217
|
+
await this._transport.close();
|
|
218
|
+
}
|
|
219
|
+
catch { /* best effort */ }
|
|
220
|
+
this._transport = null;
|
|
221
|
+
}
|
|
222
|
+
if (this._containerName) {
|
|
223
|
+
try {
|
|
224
|
+
execFileSync("docker", ["rm", "-f", this._containerName], {
|
|
225
|
+
timeout: 10_000,
|
|
226
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
catch { /* best effort */ }
|
|
230
|
+
this._containerName = null;
|
|
231
|
+
}
|
|
232
|
+
const elapsed = Date.now() - runStartTime;
|
|
233
|
+
const instanceStatus = this._aborting ? "killed" : runError ? "error" : "completed";
|
|
234
|
+
this.statusTracker?.completeInstance(this.instanceId, instanceStatus);
|
|
235
|
+
this.statusTracker?.endRun(this.agentConfig.name, elapsed, runError, tokenUsage);
|
|
236
|
+
const elapsedStr = (elapsed / 1000).toFixed(1);
|
|
237
|
+
const turnInfo = tokenUsage?.turnCount != null ? `, ${tokenUsage.turnCount} turns` : "";
|
|
238
|
+
const costInfo = tokenUsage?.cost != null ? `, $${tokenUsage.cost.toFixed(4)}` : "";
|
|
239
|
+
const errInfo = runError ? ` — ${runError.slice(0, 100)}` : "";
|
|
240
|
+
this.statusTracker?.addLogLine(this.agentConfig.name, `${this.instanceId} ${runResult} (${elapsedStr}s${turnInfo}${costInfo})${errInfo}`);
|
|
241
|
+
this.logger.info({
|
|
242
|
+
result: runResult,
|
|
243
|
+
elapsed: `${elapsedStr}s`,
|
|
244
|
+
hasReturnValue: !!returnValue,
|
|
245
|
+
turnCount: tokenUsage?.turnCount,
|
|
246
|
+
totalTokens: tokenUsage?.totalTokens,
|
|
247
|
+
cost: tokenUsage?.cost,
|
|
248
|
+
error: runError,
|
|
249
|
+
}, "run outcome");
|
|
250
|
+
if (parentSpan) {
|
|
251
|
+
parentSpan.setAttributes({
|
|
252
|
+
"execution.result": runResult,
|
|
253
|
+
"execution.has_return_value": !!returnValue,
|
|
254
|
+
});
|
|
255
|
+
if (tokenUsage) {
|
|
256
|
+
parentSpan.setAttributes({
|
|
257
|
+
"llm.token.input": tokenUsage.inputTokens,
|
|
258
|
+
"llm.token.output": tokenUsage.outputTokens,
|
|
259
|
+
"llm.token.total": tokenUsage.totalTokens,
|
|
260
|
+
"llm.cost.total": tokenUsage.cost,
|
|
261
|
+
"llm.turns": tokenUsage.turnCount,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (runResult === "error") {
|
|
265
|
+
parentSpan.recordException(new Error(`Transport execution failed: ${runError || "Unknown error"}`));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
this._running = false;
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
result: runResult,
|
|
272
|
+
triggers: [],
|
|
273
|
+
returnValue,
|
|
274
|
+
usage: tokenUsage,
|
|
275
|
+
exitReason: runError,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Create and connect the appropriate transport based on the agent's runtime config.
|
|
280
|
+
*/
|
|
281
|
+
async createTransport() {
|
|
282
|
+
const runtimeType = this.agentConfig.runtime?.type ?? "container";
|
|
283
|
+
switch (runtimeType) {
|
|
284
|
+
case "ssh": {
|
|
285
|
+
const rt = this.agentConfig.runtime;
|
|
286
|
+
if (!rt.host)
|
|
287
|
+
throw new Error("SSH runtime requires 'host' in [runtime] config");
|
|
288
|
+
const transport = new SshTransport({
|
|
289
|
+
host: rt.host,
|
|
290
|
+
port: rt.port,
|
|
291
|
+
user: rt.user,
|
|
292
|
+
keyPath: rt.key_path,
|
|
293
|
+
cwd: rt.cwd,
|
|
294
|
+
sshOptions: rt.ssh_options,
|
|
295
|
+
});
|
|
296
|
+
await transport.connect();
|
|
297
|
+
this.logger.info({ host: rt.host, user: rt.user }, "SSH transport connected");
|
|
298
|
+
return transport;
|
|
299
|
+
}
|
|
300
|
+
case "host-user": {
|
|
301
|
+
const rt = this.agentConfig.runtime ?? {};
|
|
302
|
+
const user = rt.run_as ?? "al-agent";
|
|
303
|
+
const transport = new HostUserTransport({
|
|
304
|
+
user,
|
|
305
|
+
groups: rt.groups,
|
|
306
|
+
cwd: rt.cwd,
|
|
307
|
+
});
|
|
308
|
+
await transport.connect();
|
|
309
|
+
this.logger.info({ user }, "Host-user transport connected");
|
|
310
|
+
return transport;
|
|
311
|
+
}
|
|
312
|
+
case "container":
|
|
313
|
+
default: {
|
|
314
|
+
const containerName = await this.provisionContainer();
|
|
315
|
+
this._containerName = containerName;
|
|
316
|
+
const transport = new DockerExecTransport({
|
|
317
|
+
container: containerName,
|
|
318
|
+
cwd: CONTAINER_CWD,
|
|
319
|
+
});
|
|
320
|
+
await transport.connect();
|
|
321
|
+
this.logger.info({ container: containerName }, "Docker transport connected");
|
|
322
|
+
return transport;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Provision a Docker container that stays alive for the transport to connect to.
|
|
328
|
+
* Returns the container name.
|
|
329
|
+
*/
|
|
330
|
+
async provisionContainer() {
|
|
331
|
+
const runId = randomBytes(4).toString("hex");
|
|
332
|
+
const containerName = `al-${this.agentConfig.name}-${runId}`;
|
|
333
|
+
const memory = this.globalConfig.local?.memory || "4g";
|
|
334
|
+
// Ensure the base image is available locally
|
|
335
|
+
this.ensureImageAvailable(this.baseImage);
|
|
336
|
+
const args = [
|
|
337
|
+
"run", "-d",
|
|
338
|
+
"--name", containerName,
|
|
339
|
+
"--tmpfs", "/workspace:rw,exec,nosuid",
|
|
340
|
+
"--tmpfs", "/tmp:rw,exec,nosuid",
|
|
341
|
+
"--cap-drop", "ALL",
|
|
342
|
+
"--security-opt", "no-new-privileges:true",
|
|
343
|
+
"--memory", memory,
|
|
344
|
+
];
|
|
345
|
+
if (this.globalConfig.local?.cpus) {
|
|
346
|
+
args.push("--cpus", String(this.globalConfig.local.cpus));
|
|
347
|
+
}
|
|
348
|
+
// Keep the container alive with tail -f /dev/null
|
|
349
|
+
args.push(this.baseImage, "tail", "-f", "/dev/null");
|
|
350
|
+
execFileSync("docker", args, {
|
|
351
|
+
timeout: 30_000,
|
|
352
|
+
encoding: "utf-8",
|
|
353
|
+
});
|
|
354
|
+
this.logger.info({ container: containerName }, "container provisioned");
|
|
355
|
+
return containerName;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Ensure a Docker image is available locally. If not, attempt to pull it.
|
|
359
|
+
*/
|
|
360
|
+
ensureImageAvailable(image) {
|
|
361
|
+
try {
|
|
362
|
+
execFileSync("docker", ["image", "inspect", image], {
|
|
363
|
+
timeout: 10_000,
|
|
364
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
// Image not found locally — try to pull
|
|
369
|
+
this.logger.info({ image }, "image not found locally, pulling...");
|
|
370
|
+
try {
|
|
371
|
+
execFileSync("docker", ["pull", image], {
|
|
372
|
+
timeout: 120_000,
|
|
373
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
catch (pullErr) {
|
|
377
|
+
throw new Error(`Base image "${image}" not found and pull failed: ${pullErr.message}`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Stage credentials onto the runtime via the transport.
|
|
383
|
+
* Returns a map of provider → API key for Pi session auth.
|
|
384
|
+
*/
|
|
385
|
+
async stageCredentials(transport) {
|
|
386
|
+
const providerKeys = new Map();
|
|
387
|
+
const credRefs = [...new Set(this.agentConfig.credentials ?? [])];
|
|
388
|
+
// Add provider keys for all configured models
|
|
389
|
+
for (const mc of this.agentConfig.models) {
|
|
390
|
+
if (mc.authType === "pi_auth")
|
|
391
|
+
continue;
|
|
392
|
+
const providerKey = `${mc.provider}_key`;
|
|
393
|
+
if (!credRefs.some((r) => r === providerKey || r.startsWith(`${providerKey}:`))) {
|
|
394
|
+
credRefs.push(providerKey);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (credRefs.length === 0)
|
|
398
|
+
return providerKeys;
|
|
399
|
+
// For host-user runtime, stage credentials to a temp dir (not /credentials which is root-only).
|
|
400
|
+
// For container runtimes, /credentials is inside the container filesystem.
|
|
401
|
+
const runtimeType = this.agentConfig.runtime?.type ?? "container";
|
|
402
|
+
const isHostUser = runtimeType === "host-user";
|
|
403
|
+
const credBase = isHostUser
|
|
404
|
+
? `/tmp/al-creds-${randomBytes(4).toString("hex")}`
|
|
405
|
+
: "/credentials";
|
|
406
|
+
const backend = getDefaultBackend();
|
|
407
|
+
const credFiles = new Map();
|
|
408
|
+
for (const credRef of credRefs) {
|
|
409
|
+
const { type, instance } = parseCredentialRef(credRef);
|
|
410
|
+
const fields = await backend.readAll(type, instance);
|
|
411
|
+
if (!fields)
|
|
412
|
+
continue;
|
|
413
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
414
|
+
// Stage credential file on the runtime
|
|
415
|
+
const remotePath = `${credBase}/${type}/${instance}/${field}`;
|
|
416
|
+
credFiles.set(remotePath, Buffer.from(value + "\n", "utf-8"));
|
|
417
|
+
// Extract provider API keys for Pi session auth
|
|
418
|
+
if (field === "api_key" || field === "token") {
|
|
419
|
+
if (type.endsWith("_key")) {
|
|
420
|
+
const provider = type.replace(/_key$/, "");
|
|
421
|
+
providerKeys.set(provider, value);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// Batch write all credential files
|
|
427
|
+
if (credFiles.size > 0) {
|
|
428
|
+
// Clean and recreate credential directory for a fresh state
|
|
429
|
+
await transport.exec(`rm -rf ${credBase} 2>/dev/null; mkdir -p ${credBase}`);
|
|
430
|
+
await transport.writeFiles(credFiles);
|
|
431
|
+
await transport.exec(`find ${credBase} -type f -exec chmod 400 {} +`);
|
|
432
|
+
this.logger.info({ count: credFiles.size, credBase }, "credentials staged via transport");
|
|
433
|
+
}
|
|
434
|
+
return providerKeys;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Run the Pi session in-process with transport-backed tools.
|
|
438
|
+
*/
|
|
439
|
+
async runPiSession(prompt, skillBody, transport, providerKeys) {
|
|
440
|
+
const models = this.agentConfig.models;
|
|
441
|
+
// Create resource loader with the skill body
|
|
442
|
+
const agentsContent = skillBody || `# ${this.agentConfig.name} Agent\n\nCustom agent.\n`;
|
|
443
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
444
|
+
noExtensions: true,
|
|
445
|
+
agentsFilesOverride: () => ({
|
|
446
|
+
agentsFiles: [
|
|
447
|
+
{ path: "/tmp/SKILL.md", content: agentsContent },
|
|
448
|
+
],
|
|
449
|
+
}),
|
|
450
|
+
});
|
|
451
|
+
await resourceLoader.reload();
|
|
452
|
+
const settingsManager = SettingsManager.inMemory({
|
|
453
|
+
compaction: { enabled: true },
|
|
454
|
+
retry: { enabled: true, maxRetries: 2 },
|
|
455
|
+
});
|
|
456
|
+
// Create transport-backed tools
|
|
457
|
+
const tools = createTransportTools(transport, CONTAINER_CWD);
|
|
458
|
+
// Create scheduler tools (locks, calls, status, return) if deps provided
|
|
459
|
+
let capturedReturnValue;
|
|
460
|
+
const customTools = this.schedulerToolsDeps
|
|
461
|
+
? createSchedulerTools({
|
|
462
|
+
...this.schedulerToolsDeps,
|
|
463
|
+
agentName: this.agentConfig.name,
|
|
464
|
+
instanceId: this.instanceId,
|
|
465
|
+
depth: this.depth,
|
|
466
|
+
onReturnValue: (value) => { capturedReturnValue = value; },
|
|
467
|
+
})
|
|
468
|
+
: undefined;
|
|
469
|
+
let anyModelSucceeded = false;
|
|
470
|
+
for (let pass = 0; pass <= MAX_MODEL_PASSES; pass++) {
|
|
471
|
+
const availableModels = selectAvailableModels(models, this.circuitBreaker);
|
|
472
|
+
let modelSucceeded = false;
|
|
473
|
+
for (const modelConfig of availableModels) {
|
|
474
|
+
if (this._aborting) {
|
|
475
|
+
return { result: "error", error: "Aborted" };
|
|
476
|
+
}
|
|
477
|
+
const authStorage = AuthStorage.create();
|
|
478
|
+
const providerKey = providerKeys.get(modelConfig.provider);
|
|
479
|
+
if (providerKey) {
|
|
480
|
+
authStorage.setRuntimeApiKey(modelConfig.provider, providerKey);
|
|
481
|
+
}
|
|
482
|
+
// Resolve model — either from built-in registry or custom provider with baseUrl
|
|
483
|
+
let llmModel;
|
|
484
|
+
let customModelRegistry;
|
|
485
|
+
if (modelConfig.baseUrl) {
|
|
486
|
+
// Create a model registry with the custom provider registered
|
|
487
|
+
customModelRegistry = ModelRegistry.inMemory(authStorage);
|
|
488
|
+
const providerName = `custom_${modelConfig.provider}`;
|
|
489
|
+
// Register API key under custom provider name too
|
|
490
|
+
if (providerKey) {
|
|
491
|
+
authStorage.setRuntimeApiKey(providerName, providerKey);
|
|
492
|
+
}
|
|
493
|
+
customModelRegistry.registerProvider(providerName, {
|
|
494
|
+
baseUrl: modelConfig.baseUrl,
|
|
495
|
+
apiKey: providerKey || "dummy-key",
|
|
496
|
+
models: [{
|
|
497
|
+
id: modelConfig.model,
|
|
498
|
+
name: modelConfig.model,
|
|
499
|
+
api: "openai-completions",
|
|
500
|
+
reasoning: false,
|
|
501
|
+
input: ["text"],
|
|
502
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
503
|
+
contextWindow: 128000,
|
|
504
|
+
maxTokens: 16384,
|
|
505
|
+
}],
|
|
506
|
+
});
|
|
507
|
+
llmModel = customModelRegistry.find(providerName, modelConfig.model);
|
|
508
|
+
if (!llmModel) {
|
|
509
|
+
throw new Error(`Failed to register custom model ${modelConfig.provider}/${modelConfig.model} at ${modelConfig.baseUrl}`);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
llmModel = getModel(modelConfig.provider, modelConfig.model);
|
|
514
|
+
}
|
|
515
|
+
this.logger.info({
|
|
516
|
+
model: modelConfig.model,
|
|
517
|
+
thinking: modelConfig.thinkingLevel,
|
|
518
|
+
baseUrl: modelConfig.baseUrl,
|
|
519
|
+
}, "creating Pi session with transport tools");
|
|
520
|
+
const { session } = await createAgentSession({
|
|
521
|
+
cwd: CONTAINER_CWD,
|
|
522
|
+
model: llmModel,
|
|
523
|
+
modelRegistry: customModelRegistry,
|
|
524
|
+
thinkingLevel: modelConfig.thinkingLevel,
|
|
525
|
+
authStorage,
|
|
526
|
+
resourceLoader,
|
|
527
|
+
tools,
|
|
528
|
+
customTools,
|
|
529
|
+
sessionManager: SessionManager.inMemory(),
|
|
530
|
+
settingsManager,
|
|
531
|
+
});
|
|
532
|
+
this._session = session;
|
|
533
|
+
// Subscribe to events for logging and status tracking
|
|
534
|
+
this.subscribeToEvents(session);
|
|
535
|
+
try {
|
|
536
|
+
const state = session.state;
|
|
537
|
+
this.logger.debug({
|
|
538
|
+
promptLength: prompt.length,
|
|
539
|
+
model: state?.model ? { id: state.model.id, provider: state.model.provider, api: state.model.api, baseUrl: state.model.baseUrl } : "NO MODEL",
|
|
540
|
+
toolCount: state?.tools?.length ?? 0,
|
|
541
|
+
}, "about to call session.prompt()");
|
|
542
|
+
await session.prompt(prompt);
|
|
543
|
+
const allMessages = session.state?.messages ?? [];
|
|
544
|
+
const lastMsg = allMessages[allMessages.length - 1];
|
|
545
|
+
if (lastMsg?.stopReason === "error") {
|
|
546
|
+
this.logger.error({
|
|
547
|
+
errorMessage: lastMsg?.errorMessage ?? session.state?.errorMessage,
|
|
548
|
+
}, "session.prompt() completed with error");
|
|
549
|
+
}
|
|
550
|
+
this.circuitBreaker.recordSuccess(modelConfig.provider, modelConfig.model);
|
|
551
|
+
// Get usage stats
|
|
552
|
+
const sessionStats = session.getSessionStats();
|
|
553
|
+
const usage = sessionStatsToUsage(sessionStats);
|
|
554
|
+
this.logger.info({
|
|
555
|
+
inputTokens: usage.inputTokens,
|
|
556
|
+
outputTokens: usage.outputTokens,
|
|
557
|
+
turnCount: usage.turnCount,
|
|
558
|
+
cost: usage.cost,
|
|
559
|
+
}, "session completed");
|
|
560
|
+
session.dispose();
|
|
561
|
+
this._session = null;
|
|
562
|
+
modelSucceeded = true;
|
|
563
|
+
anyModelSucceeded = true;
|
|
564
|
+
return { result: "completed", usage, returnValue: capturedReturnValue };
|
|
565
|
+
}
|
|
566
|
+
catch (promptErr) {
|
|
567
|
+
const msg = String(promptErr?.message || promptErr || "");
|
|
568
|
+
if (isRateLimitError(msg)) {
|
|
569
|
+
this.circuitBreaker.recordFailure(modelConfig.provider, modelConfig.model);
|
|
570
|
+
this.logger.warn({
|
|
571
|
+
provider: modelConfig.provider,
|
|
572
|
+
model: modelConfig.model,
|
|
573
|
+
}, "rate limited, trying next model");
|
|
574
|
+
session.dispose();
|
|
575
|
+
this._session = null;
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
session.dispose();
|
|
579
|
+
this._session = null;
|
|
580
|
+
return { result: "error", error: msg };
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (modelSucceeded)
|
|
584
|
+
break;
|
|
585
|
+
if (pass < MAX_MODEL_PASSES) {
|
|
586
|
+
const delayMs = Math.min(DEFAULT_BACKOFF_MS * Math.pow(2, pass), MAX_BACKOFF_MS);
|
|
587
|
+
this.logger.warn({ pass: pass + 1, delayMs }, "all models exhausted, backing off");
|
|
588
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (!anyModelSucceeded) {
|
|
592
|
+
this.logger.error("all models exhausted across all retry passes");
|
|
593
|
+
return { result: "error", error: "All models exhausted — rate limited across all retries" };
|
|
594
|
+
}
|
|
595
|
+
return { result: "completed" };
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Subscribe to Pi session events and forward them to the logger and status tracker.
|
|
599
|
+
* Since the session runs in-process, we get direct access to events.
|
|
600
|
+
*/
|
|
601
|
+
subscribeToEvents(session) {
|
|
602
|
+
session.subscribe((event) => {
|
|
603
|
+
// Log all event types at debug level
|
|
604
|
+
this.logger.debug({ eventType: event.type }, "session event");
|
|
605
|
+
if (event.type === "tool_execution_start") {
|
|
606
|
+
const cmd = String(event.args?.command || "");
|
|
607
|
+
this.logger.debug({
|
|
608
|
+
toolName: event.toolName,
|
|
609
|
+
toolCallId: event.toolCallId,
|
|
610
|
+
command: cmd || undefined,
|
|
611
|
+
}, "tool started");
|
|
612
|
+
}
|
|
613
|
+
if (event.type === "tool_execution_end") {
|
|
614
|
+
const resultStr = typeof event.result === "string"
|
|
615
|
+
? event.result
|
|
616
|
+
: JSON.stringify(event.result);
|
|
617
|
+
if (event.isError) {
|
|
618
|
+
const cmdPrefix = event.args?.command ? `$ ${String(event.args.command).slice(0, 80)} — ` : "";
|
|
619
|
+
this.statusTracker?.setAgentError(this.agentConfig.name, `${cmdPrefix}${resultStr.slice(0, 200)}`);
|
|
620
|
+
}
|
|
621
|
+
this.logger.debug({
|
|
622
|
+
toolName: event.toolName,
|
|
623
|
+
toolCallId: event.toolCallId,
|
|
624
|
+
isError: !!event.isError,
|
|
625
|
+
resultLength: resultStr.length,
|
|
626
|
+
}, "tool ended");
|
|
627
|
+
}
|
|
628
|
+
if (event.type === "error") {
|
|
629
|
+
const err = event.error;
|
|
630
|
+
const errorMsg = err?.errorMessage
|
|
631
|
+
|| (typeof err === "string" ? err : null)
|
|
632
|
+
|| JSON.stringify(event);
|
|
633
|
+
this.logger.error({ error: errorMsg }, "session error");
|
|
634
|
+
this.statusTracker?.setAgentError(this.agentConfig.name, String(errorMsg).slice(0, 200));
|
|
635
|
+
}
|
|
636
|
+
// Context usage tracking
|
|
637
|
+
if (event.type === "turn_end") {
|
|
638
|
+
try {
|
|
639
|
+
const ctx = session.getContextUsage?.();
|
|
640
|
+
if (ctx && ctx.percent != null) {
|
|
641
|
+
this.logger.info({
|
|
642
|
+
contextPercent: Math.round(ctx.percent * 10) / 10,
|
|
643
|
+
contextWindow: ctx.contextWindow,
|
|
644
|
+
contextTokens: ctx.tokens,
|
|
645
|
+
}, "context-usage");
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
catch { /* context usage not available */ }
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
//# sourceMappingURL=transport-runner.js.map
|