@themoltnet/agent-daemon 0.57.0 → 0.58.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/README.md +21 -0
- package/dist/cli.js +543 -342
- package/package.json +9 -9
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as cryptoService, defaultPiDaemonAdapter, i as compileExecutionPlan, n as createExecutionPlanSnapshot, r as parseCredentialRequirements, t as runtimeExecutionOffer } from "./pi.js";
|
|
3
2
|
import { assertRuntimeAdapterSupportsProfile } from "./runtime.js";
|
|
3
|
+
import { a as cryptoService, defaultPiDaemonAdapter, i as compileExecutionPlan, n as createExecutionPlanSnapshot, r as parseCredentialRequirements, t as runtimeExecutionOffer } from "./pi.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { pino, transport } from "pino";
|
|
6
6
|
import { Type } from "typebox";
|
|
@@ -10,7 +10,7 @@ import "multiformats/hashes/sha2";
|
|
|
10
10
|
import "typebox/value";
|
|
11
11
|
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
12
12
|
import { parseArgs, promisify } from "node:util";
|
|
13
|
-
import { AgentRuntime, ApiTaskReporter, ApiTaskSource, PollingApiTaskSource, createLocalSeedSigner, resolveAgentIdentity,
|
|
13
|
+
import { AgentRuntime, ApiTaskReporter, ApiTaskSource, PollingApiTaskSource, createLocalSeedSigner, resolveAgentIdentity, resolveRuntimeProfile, resolveRuntimeProfiles, validateRuntimeProfilePrerequisites } from "@themoltnet/agent-runtime";
|
|
14
14
|
import { GuestEnvironmentBoundaryError, assertGuestEnvironmentBoundary, createPiRetryTriage, findMainWorktree, isResolvedPathInsideRoot, normalizeRetryTriageResult, redactRetryTriageSecrets, resolveRuntimeProfileModel } from "@themoltnet/pi-runtime";
|
|
15
15
|
import { FILE_SECRET_PROVIDER, FileSecretProvider, RegisterIdentityError, boundedIdentitySignal, connect, createNodeSecretProviderRegistry, register } from "@themoltnet/sdk/node";
|
|
16
16
|
import { constants, copyFileSync, createReadStream, createWriteStream, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
@@ -27,7 +27,7 @@ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
|
27
27
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
28
28
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
29
29
|
import { homedir, platform, tmpdir } from "node:os";
|
|
30
|
-
import { writePiConfig } from "@themoltnet/pi-runtime/pi-config";
|
|
30
|
+
import { PI_MODEL_MODALITIES, writePiConfig } from "@themoltnet/pi-runtime/pi-config";
|
|
31
31
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
32
32
|
import { mkdir, open, readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
33
33
|
import { pipeline } from "node:stream/promises";
|
|
@@ -1937,18 +1937,6 @@ var RuntimeProfileRef = Type.Object({ profileId: Type.String({ format: "uuid" })
|
|
|
1937
1937
|
$id: "RuntimeProfileRef",
|
|
1938
1938
|
additionalProperties: false
|
|
1939
1939
|
});
|
|
1940
|
-
var RuntimeProfileLeaseTtlSec = Type.Integer({
|
|
1941
|
-
minimum: 1,
|
|
1942
|
-
maximum: 86400
|
|
1943
|
-
});
|
|
1944
|
-
var RuntimeProfileHeartbeatIntervalMs = Type.Integer({
|
|
1945
|
-
minimum: 0,
|
|
1946
|
-
maximum: 36e5
|
|
1947
|
-
});
|
|
1948
|
-
var RuntimeProfileMaxBatchSize = Type.Integer({
|
|
1949
|
-
minimum: 1,
|
|
1950
|
-
maximum: 1e3
|
|
1951
|
-
});
|
|
1952
1940
|
var RuntimeProfileMaxTurns = Type.Integer({
|
|
1953
1941
|
minimum: 0,
|
|
1954
1942
|
maximum: 1e4
|
|
@@ -1977,21 +1965,8 @@ Type.Object({
|
|
|
1977
1965
|
maxOutputTokens: RuntimeProfileNullableMaxOutputTokens,
|
|
1978
1966
|
runtimeKind: RuntimeProfileRuntimeKind,
|
|
1979
1967
|
sandbox: RuntimeProfileSandbox,
|
|
1980
|
-
sessionStorageMode: Type.Literal("local"),
|
|
1981
|
-
workspaceStorageMode: Type.Literal("local"),
|
|
1982
1968
|
defaultWorkspaceMode: Type.Union([RuntimeProfileWorkspaceMode, Type.Null()]),
|
|
1983
1969
|
allowedWorkspaceModes: RuntimeProfileAllowedWorkspaceModes,
|
|
1984
|
-
sessionTtlSec: Type.Integer({
|
|
1985
|
-
minimum: 1,
|
|
1986
|
-
maximum: 86400
|
|
1987
|
-
}),
|
|
1988
|
-
workspaceTtlSec: Type.Integer({
|
|
1989
|
-
minimum: 1,
|
|
1990
|
-
maximum: 86400
|
|
1991
|
-
}),
|
|
1992
|
-
leaseTtlSec: RuntimeProfileLeaseTtlSec,
|
|
1993
|
-
heartbeatIntervalMs: RuntimeProfileHeartbeatIntervalMs,
|
|
1994
|
-
maxBatchSize: RuntimeProfileMaxBatchSize,
|
|
1995
1970
|
maxTurns: RuntimeProfileMaxTurns,
|
|
1996
1971
|
maxBashTimeouts: RuntimeProfileMaxBashTimeouts,
|
|
1997
1972
|
toolEnforcement: RuntimeProfileToolEnforcement,
|
|
@@ -2124,6 +2099,7 @@ var ResolvedRuntimeSlot = Type.Object({
|
|
|
2124
2099
|
workspace: Type.Union([RuntimeWorkspace, Type.Null()])
|
|
2125
2100
|
}, { $id: "ResolvedRuntimeSlot" });
|
|
2126
2101
|
Type.Object({ items: Type.Array(ResolvedRuntimeSlot) }, { $id: "RuntimeSlotListResponse" });
|
|
2102
|
+
var MAX_RUNTIME_WARM_RETENTION_SEC = 86400;
|
|
2127
2103
|
Type.Object({
|
|
2128
2104
|
agentName: Type.String({
|
|
2129
2105
|
minLength: 1,
|
|
@@ -2150,7 +2126,11 @@ Type.Object({
|
|
|
2150
2126
|
worktreeBranch: Type.Optional(Type.String({ minLength: 1 })),
|
|
2151
2127
|
workspaceKind: Type.Optional(RuntimeWorkspaceKind),
|
|
2152
2128
|
lastTaskId: Type.String({ format: "uuid" }),
|
|
2153
|
-
lastAttemptN: Type.Integer({ minimum: 1 })
|
|
2129
|
+
lastAttemptN: Type.Integer({ minimum: 1 }),
|
|
2130
|
+
warmRetentionSec: Type.Integer({
|
|
2131
|
+
minimum: 0,
|
|
2132
|
+
maximum: MAX_RUNTIME_WARM_RETENTION_SEC
|
|
2133
|
+
})
|
|
2154
2134
|
}, {
|
|
2155
2135
|
$id: "BeginRuntimeSlotBody",
|
|
2156
2136
|
additionalProperties: false
|
|
@@ -2172,7 +2152,11 @@ Type.Object({
|
|
|
2172
2152
|
slotKey: Type.String({ minLength: 1 }),
|
|
2173
2153
|
taskId: Type.String({ format: "uuid" }),
|
|
2174
2154
|
attemptN: Type.Integer({ minimum: 1 }),
|
|
2175
|
-
sessionPath: Type.Optional(Type.String({ minLength: 1 }))
|
|
2155
|
+
sessionPath: Type.Optional(Type.String({ minLength: 1 })),
|
|
2156
|
+
warmRetentionSec: Type.Integer({
|
|
2157
|
+
minimum: 0,
|
|
2158
|
+
maximum: MAX_RUNTIME_WARM_RETENTION_SEC
|
|
2159
|
+
})
|
|
2176
2160
|
}, {
|
|
2177
2161
|
$id: "FinishRuntimeSlotBody",
|
|
2178
2162
|
additionalProperties: false
|
|
@@ -3461,7 +3445,7 @@ var COMMON_REQUIRED_FLAGS = `\
|
|
|
3461
3445
|
--profile <uuid|name> Remote runtime profile. Repeat for poll/drain
|
|
3462
3446
|
to declare priority order. Provider, model,
|
|
3463
3447
|
sandbox policy, prerequisites, and runtime
|
|
3464
|
-
|
|
3448
|
+
execution policy come from the selected profile.`;
|
|
3465
3449
|
var COMMON_OPTIONAL_FLAGS = `\
|
|
3466
3450
|
--sandbox <path> Deprecated. Remote runtime profiles define
|
|
3467
3451
|
sandbox policy.
|
|
@@ -3473,24 +3457,10 @@ var COMMON_OPTIONAL_FLAGS = `\
|
|
|
3473
3457
|
host git config. Configless agent-key runs must
|
|
3474
3458
|
provide this flag or MOLTNET_GIT_AUTHOR.
|
|
3475
3459
|
Env: MOLTNET_GIT_AUTHOR.
|
|
3476
|
-
--lease-ttl-sec <n> Sliding liveness window. Silence longer than
|
|
3477
|
-
this ends the attempt with lease_expired.
|
|
3478
|
-
Default: 300.
|
|
3479
3460
|
--heartbeat-interval-ms <n> Reporter heartbeat cadence. Default: 60000.
|
|
3480
|
-
--
|
|
3481
|
-
--flush-interval-ms <n> Reporter flush window. Default: 200.
|
|
3482
|
-
--max-turns <n> Cap on tool-use turns per attempt; over this,
|
|
3483
|
-
the pi session aborts and the attempt fails
|
|
3484
|
-
with code 'max_turns_exceeded'. 0 = disabled.
|
|
3485
|
-
Default: 0. Recommended ~30 for fulfill_brief.
|
|
3486
|
-
--max-bash-timeouts <n> Cap on bash-tool timeouts per attempt; over
|
|
3487
|
-
this, the pi session aborts and the attempt
|
|
3488
|
-
fails with code 'max_bash_timeouts_exceeded'.
|
|
3489
|
-
0 = disabled. Default: 3.
|
|
3490
|
-
--warm-session-ttl-sec <n> Resumability window for runtime slots
|
|
3461
|
+
--warm-retention-sec <n> Resumability window for runtime slots
|
|
3491
3462
|
(Pi sessions + reusable worktrees) after use.
|
|
3492
|
-
Default: 1800
|
|
3493
|
-
TTL) when --profile is set.
|
|
3463
|
+
Default: 1800.
|
|
3494
3464
|
--debug Verbose logging: also log successful list/claim
|
|
3495
3465
|
outcomes (candidate counts, claim attempts).`;
|
|
3496
3466
|
var REGISTERED_TASK_TYPES = Object.keys(BUILT_IN_TASK_TYPES).sort();
|
|
@@ -3681,10 +3651,17 @@ Options:
|
|
|
3681
3651
|
(or MOLTNET_AGENT_SERVER_ROOT).
|
|
3682
3652
|
--api-url <url> Default MoltNet API for new managed agents.
|
|
3683
3653
|
Default: https://api.themolt.net.
|
|
3654
|
+
--heartbeat-interval-ms <n> Child reporter heartbeat cadence. Default: 60000.
|
|
3655
|
+
--warm-retention-sec <n> Child session/workspace retention. Default: 1800.
|
|
3656
|
+
--supervised Also stop gracefully when stdin reaches EOF.
|
|
3684
3657
|
|
|
3685
3658
|
On macOS, the first interactive run asks to trust a per-user local CA in the
|
|
3686
|
-
login keychain and serves HTTPS.
|
|
3687
|
-
|
|
3659
|
+
login keychain and serves HTTPS. Native supervisors use:
|
|
3660
|
+
server trust --status --json
|
|
3661
|
+
server trust --yes --json
|
|
3662
|
+
server trust --remove --yes --json
|
|
3663
|
+
Run \`agent-daemon server trust --remove\` interactively to remove that exact
|
|
3664
|
+
CA. Linux continues to use the Chromium PNA HTTP path.
|
|
3688
3665
|
`;
|
|
3689
3666
|
var PROVIDERS_HELP = `\
|
|
3690
3667
|
moltnet-agent providers — manage local model providers.
|
|
@@ -3693,6 +3670,7 @@ Usage:
|
|
|
3693
3670
|
moltnet-agent providers list [--json] [--root <path>]
|
|
3694
3671
|
moltnet-agent providers set <id> [--base-url <url>] [--api <pi-api-kind>]
|
|
3695
3672
|
[--model <id> ... | --clear-models]
|
|
3673
|
+
[--model-input <id>=text,image ...]
|
|
3696
3674
|
[--api-key-stdin | --clear-api-key] [--root <path>]
|
|
3697
3675
|
moltnet-agent providers discover <id> [--save] [--json] [--root <path>]
|
|
3698
3676
|
moltnet-agent providers remove <id> [--yes] [--root <path>]
|
|
@@ -3703,6 +3681,11 @@ Usage:
|
|
|
3703
3681
|
The default root is ~/.config/moltnet. MOLTNET_AGENT_SERVER_ROOT remains the
|
|
3704
3682
|
environment override. API keys are accepted only from redirected stdin; they
|
|
3705
3683
|
are stored separately and providers.json contains only a secret reference.
|
|
3684
|
+
|
|
3685
|
+
--model declares a text-only model. --model-input declares a model together
|
|
3686
|
+
with the input modalities it accepts, and is what makes a vision model usable:
|
|
3687
|
+
a model with no declared modalities is text-only to Pi, which drops image
|
|
3688
|
+
content parts before the request leaves the runtime.
|
|
3706
3689
|
`;
|
|
3707
3690
|
//#endregion
|
|
3708
3691
|
//#region src/lib/identity-pin.ts
|
|
@@ -4189,10 +4172,10 @@ function slugifySessionComponent(input) {
|
|
|
4189
4172
|
}
|
|
4190
4173
|
//#endregion
|
|
4191
4174
|
//#region src/lib/task-execution-plan.ts
|
|
4192
|
-
function buildDaemonTaskExecutionPlan(task, stateDirs, identity,
|
|
4175
|
+
function buildDaemonTaskExecutionPlan(task, stateDirs, identity, warmRetentionSec, runtimeProfileWorkspacePolicy = {}, attemptN) {
|
|
4193
4176
|
const descriptor = deriveTaskSessionDescriptor(task);
|
|
4194
4177
|
const workspaceMode = resolveTaskWorkspaceMode(task, descriptor.policy, runtimeProfileWorkspacePolicy);
|
|
4195
|
-
const slotKey =
|
|
4178
|
+
const slotKey = warmRetentionSec > 0 && descriptor.sessionKey ? buildRuntimeSlotKey(descriptor.sessionKey, identity.runtimeInstanceId) : null;
|
|
4196
4179
|
const workspaceScope = slotKey !== null ? descriptor.policy.workspaceScope : "attempt";
|
|
4197
4180
|
const slotId = slotKey ? buildDaemonSlotId(identity, slotKey) : null;
|
|
4198
4181
|
const sessionDir = slotId ? `${stateDirs.piSessionsDir}/${boundedKeyDirComponent(slotId)}` : null;
|
|
@@ -4331,7 +4314,7 @@ function createExecutionPlanCache(args) {
|
|
|
4331
4314
|
const key = buildClaimedTaskKey(claimedTask);
|
|
4332
4315
|
const existing = cache.get(key);
|
|
4333
4316
|
if (existing) return existing;
|
|
4334
|
-
const plan = await maybeAttachWarmSlotContext(claimedTask, buildDaemonTaskExecutionPlan(claimedTask.task, args.stateDirs, args.slotIdentity, args.
|
|
4317
|
+
const plan = await maybeAttachWarmSlotContext(claimedTask, buildDaemonTaskExecutionPlan(claimedTask.task, args.stateDirs, args.slotIdentity, args.warmRetentionSec, args.workspacePolicy, claimedTask.attemptN), args.stateDirs, args.slotRegistry, runtimeSessionStore, sourceAttemptResolver);
|
|
4335
4318
|
assertPlanAllowedByWorkspacePolicy(plan, args.workspacePolicy, args.slotIdentity.runtimeProfileId);
|
|
4336
4319
|
cache.set(key, plan);
|
|
4337
4320
|
return plan;
|
|
@@ -5022,7 +5005,8 @@ function ownBinding(bindings, name) {
|
|
|
5022
5005
|
}
|
|
5023
5006
|
//#endregion
|
|
5024
5007
|
//#region ../../libs/execution-integrations/src/runtime-profile.ts
|
|
5025
|
-
var CURRENT_EFFECTIVE_POLICY_SNAPSHOT_VERSION = "effective-policy:
|
|
5008
|
+
var CURRENT_EFFECTIVE_POLICY_SNAPSHOT_VERSION = "effective-policy:v2";
|
|
5009
|
+
var DEFAULT_EXECUTION_LEASE_TTL_SEC = 300;
|
|
5026
5010
|
/**
|
|
5027
5011
|
* Maps resolved product authority into portable intent. Policy composition is
|
|
5028
5012
|
* deliberately upstream: this boundary accepts no policy IDs and performs no
|
|
@@ -5046,7 +5030,7 @@ function executionIntentFromRuntimeProfile(input) {
|
|
|
5046
5030
|
credentialRequirements: structuredClone(input.credentialRequirements),
|
|
5047
5031
|
requiredCapabilities: [...input.requiredCapabilities ?? []],
|
|
5048
5032
|
lease: {
|
|
5049
|
-
ttlSec:
|
|
5033
|
+
ttlSec: DEFAULT_EXECUTION_LEASE_TTL_SEC,
|
|
5050
5034
|
requiredControls: [...input.requiredLeaseControls ?? []]
|
|
5051
5035
|
},
|
|
5052
5036
|
network: {
|
|
@@ -5296,14 +5280,9 @@ async function logDaemonStartupFailure(input) {
|
|
|
5296
5280
|
}
|
|
5297
5281
|
//#endregion
|
|
5298
5282
|
//#region src/lib/options.ts
|
|
5299
|
-
var
|
|
5300
|
-
leaseTtlSec: 300,
|
|
5283
|
+
var DEFAULT_LOCAL_OPERATIONAL_SETTINGS = {
|
|
5301
5284
|
heartbeatIntervalMs: 6e4,
|
|
5302
|
-
|
|
5303
|
-
flushIntervalMs: 200,
|
|
5304
|
-
maxTurns: 0,
|
|
5305
|
-
maxBashTimeouts: 3,
|
|
5306
|
-
warmSessionTtlSec: 1800
|
|
5285
|
+
warmRetentionSec: 1800
|
|
5307
5286
|
};
|
|
5308
5287
|
var MissingRequiredOptionError = class extends Error {
|
|
5309
5288
|
constructor(flag) {
|
|
@@ -5312,42 +5291,33 @@ var MissingRequiredOptionError = class extends Error {
|
|
|
5312
5291
|
this.name = "MissingRequiredOptionError";
|
|
5313
5292
|
}
|
|
5314
5293
|
};
|
|
5315
|
-
function
|
|
5316
|
-
const runtimeDefaults = {
|
|
5317
|
-
leaseTtlSec: options.runtimeDefaults?.leaseTtlSec ?? DEFAULTS.leaseTtlSec,
|
|
5318
|
-
heartbeatIntervalMs: options.runtimeDefaults?.heartbeatIntervalMs ?? DEFAULTS.heartbeatIntervalMs,
|
|
5319
|
-
maxBatchSize: options.runtimeDefaults?.maxBatchSize ?? DEFAULTS.maxBatchSize,
|
|
5320
|
-
maxTurns: options.runtimeDefaults?.maxTurns ?? DEFAULTS.maxTurns,
|
|
5321
|
-
maxBashTimeouts: options.runtimeDefaults?.maxBashTimeouts ?? DEFAULTS.maxBashTimeouts,
|
|
5322
|
-
warmSessionTtlSec: options.runtimeDefaults?.warmSessionTtlSec ?? DEFAULTS.warmSessionTtlSec
|
|
5323
|
-
};
|
|
5294
|
+
function parseIdentityProcessOptions(args) {
|
|
5324
5295
|
if (!args.agent) throw new MissingRequiredOptionError("agent");
|
|
5325
5296
|
if (!/^[a-zA-Z0-9_-]+$/.test(args.agent)) throw new Error(`Invalid --agent "${args.agent}": must match /^[a-zA-Z0-9_-]+$/`);
|
|
5326
5297
|
return {
|
|
5327
5298
|
agent: args.agent,
|
|
5328
|
-
leaseTtlSec: parsePositiveInt(args["lease-ttl-sec"], "lease-ttl-sec", runtimeDefaults.leaseTtlSec),
|
|
5329
|
-
heartbeatIntervalMs: parseNonNegativeInt(args["heartbeat-interval-ms"], "heartbeat-interval-ms", runtimeDefaults.heartbeatIntervalMs),
|
|
5330
|
-
maxBatchSize: parsePositiveInt(args["max-batch-size"], "max-batch-size", runtimeDefaults.maxBatchSize),
|
|
5331
|
-
flushIntervalMs: parseNonNegativeInt(args["flush-interval-ms"], "flush-interval-ms", DEFAULTS.flushIntervalMs),
|
|
5332
|
-
maxTurns: parseNonNegativeInt(args["max-turns"], "max-turns", runtimeDefaults.maxTurns),
|
|
5333
|
-
maxBashTimeouts: parseNonNegativeInt(args["max-bash-timeouts"], "max-bash-timeouts", runtimeDefaults.maxBashTimeouts),
|
|
5334
|
-
warmSessionTtlSec: parseNonNegativeInt(args["warm-session-ttl-sec"], "warm-session-ttl-sec", runtimeDefaults.warmSessionTtlSec),
|
|
5335
5299
|
debug: args.debug === true
|
|
5336
5300
|
};
|
|
5337
5301
|
}
|
|
5338
|
-
function
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5302
|
+
function parseLocalOperationalSettings(args) {
|
|
5303
|
+
return {
|
|
5304
|
+
heartbeatIntervalMs: parseNonNegativeInt(args["heartbeat-interval-ms"], "heartbeat-interval-ms", DEFAULT_LOCAL_OPERATIONAL_SETTINGS.heartbeatIntervalMs),
|
|
5305
|
+
warmRetentionSec: parseNonNegativeInt(args["warm-retention-sec"], "warm-retention-sec", DEFAULT_LOCAL_OPERATIONAL_SETTINGS.warmRetentionSec, MAX_RUNTIME_WARM_RETENTION_SEC)
|
|
5306
|
+
};
|
|
5307
|
+
}
|
|
5308
|
+
function parseRuntimeCommandOptions(args) {
|
|
5309
|
+
return {
|
|
5310
|
+
identity: parseIdentityProcessOptions(args),
|
|
5311
|
+
operations: parseLocalOperationalSettings(args)
|
|
5312
|
+
};
|
|
5343
5313
|
}
|
|
5344
|
-
function parseNonNegativeInt(raw, name, defaultValue) {
|
|
5314
|
+
function parseNonNegativeInt(raw, name, defaultValue, maximum = Number.MAX_SAFE_INTEGER) {
|
|
5345
5315
|
if (raw === void 0) return defaultValue;
|
|
5346
|
-
const
|
|
5347
|
-
if (!Number.isInteger(
|
|
5348
|
-
return
|
|
5316
|
+
const value = Number(raw);
|
|
5317
|
+
if (!Number.isInteger(value) || value < 0 || value > maximum) throw new Error(`Invalid --${name} "${raw}": must be a non-negative integer no greater than ${maximum}`);
|
|
5318
|
+
return value;
|
|
5349
5319
|
}
|
|
5350
|
-
function
|
|
5320
|
+
function identityOptionDefs() {
|
|
5351
5321
|
return {
|
|
5352
5322
|
agent: {
|
|
5353
5323
|
type: "string",
|
|
@@ -5355,18 +5325,18 @@ function commonOptionDefs() {
|
|
|
5355
5325
|
},
|
|
5356
5326
|
"agent-root": { type: "string" },
|
|
5357
5327
|
"git-author": { type: "string" },
|
|
5358
|
-
"lease-ttl-sec": { type: "string" },
|
|
5359
|
-
"heartbeat-interval-ms": { type: "string" },
|
|
5360
|
-
"max-batch-size": { type: "string" },
|
|
5361
|
-
"flush-interval-ms": { type: "string" },
|
|
5362
|
-
"max-turns": { type: "string" },
|
|
5363
|
-
"max-bash-timeouts": { type: "string" },
|
|
5364
|
-
"warm-session-ttl-sec": { type: "string" },
|
|
5365
5328
|
debug: { type: "boolean" }
|
|
5366
5329
|
};
|
|
5367
5330
|
}
|
|
5331
|
+
function runtimeCommandOptionDefs() {
|
|
5332
|
+
return {
|
|
5333
|
+
...identityOptionDefs(),
|
|
5334
|
+
"heartbeat-interval-ms": { type: "string" },
|
|
5335
|
+
"warm-retention-sec": { type: "string" }
|
|
5336
|
+
};
|
|
5337
|
+
}
|
|
5368
5338
|
function validateTaskTypes(types) {
|
|
5369
|
-
const unknown = types.filter((
|
|
5339
|
+
const unknown = types.filter((taskType) => !Object.prototype.hasOwnProperty.call(BUILT_IN_TASK_TYPES, taskType));
|
|
5370
5340
|
if (unknown.length > 0) throw new Error(`Unknown task type(s): ${unknown.join(", ")}. Known types: ${knownTaskTypesList()}.`);
|
|
5371
5341
|
return [...types];
|
|
5372
5342
|
}
|
|
@@ -5465,6 +5435,17 @@ function resolveAgentServerRoot(input) {
|
|
|
5465
5435
|
if (override) return override;
|
|
5466
5436
|
return getConfigDir();
|
|
5467
5437
|
}
|
|
5438
|
+
/**
|
|
5439
|
+
* Copy a model entry, dropping an empty `input` so it never reaches the wire.
|
|
5440
|
+
* Not a compatibility shim: `providers.json` has exactly one model shape, and
|
|
5441
|
+
* `validateProviders` rejects anything else on read.
|
|
5442
|
+
*/
|
|
5443
|
+
function copyProviderModel(entry) {
|
|
5444
|
+
return {
|
|
5445
|
+
id: entry.id,
|
|
5446
|
+
...entry.input && entry.input.length > 0 ? { input: [...entry.input] } : {}
|
|
5447
|
+
};
|
|
5448
|
+
}
|
|
5468
5449
|
function providerEnvName(providerId) {
|
|
5469
5450
|
return `MOLTNET_PROVIDER_${assertProviderId(providerId).replaceAll("-", "_").toUpperCase()}_API_KEY`;
|
|
5470
5451
|
}
|
|
@@ -5663,6 +5644,7 @@ var AgentServerStore = class {
|
|
|
5663
5644
|
for (const [id, provider] of Object.entries(state)) {
|
|
5664
5645
|
assertProviderId(id);
|
|
5665
5646
|
assertProviderEnvName(id, provider.envName);
|
|
5647
|
+
for (const model of provider.models ?? []) if (typeof model !== "object" || model === null || !model.id) throw new AgentServerStoreError("invalid_state", `provider "${id}" has a model entry that is not { id, input? }; rewrite providers.json entries as objects`);
|
|
5666
5648
|
}
|
|
5667
5649
|
}
|
|
5668
5650
|
runDir(id) {
|
|
@@ -5936,6 +5918,64 @@ function mergePiModels(store, repo) {
|
|
|
5936
5918
|
return { providers };
|
|
5937
5919
|
}
|
|
5938
5920
|
//#endregion
|
|
5921
|
+
//#region src/lib/state-dir.ts
|
|
5922
|
+
function ensureDaemonStateDirs(mountPath) {
|
|
5923
|
+
const rootDir = join(mountPath, ".moltnet", "d");
|
|
5924
|
+
const piSessionsDir = join(rootDir, "pi-sessions");
|
|
5925
|
+
mkdirSync(piSessionsDir, { recursive: true });
|
|
5926
|
+
return {
|
|
5927
|
+
rootDir,
|
|
5928
|
+
piSessionsDir
|
|
5929
|
+
};
|
|
5930
|
+
}
|
|
5931
|
+
//#endregion
|
|
5932
|
+
//#region src/lib/prepare-runtime-profile.ts
|
|
5933
|
+
/** Validate and prepare a profile through the shared daemon execution path. */
|
|
5934
|
+
async function prepareRuntimeProfile(input) {
|
|
5935
|
+
const { profile } = input;
|
|
5936
|
+
assertRuntimeAdapterSupportsProfile(input.runtimeAdapter, profile);
|
|
5937
|
+
const preparedRuntime = attestPreparedRuntime(await input.runtimeAdapter.prepare({ profile }), input.signingPrivateKey);
|
|
5938
|
+
validateRuntimeProfilePrerequisites(profile, input.prerequisiteEnv, {
|
|
5939
|
+
tools: preparedRuntime.tools,
|
|
5940
|
+
executables: preparedRuntime.executables
|
|
5941
|
+
});
|
|
5942
|
+
assertGuestEnvironmentBoundary({
|
|
5943
|
+
forwardEnv: profile.requiredEnv,
|
|
5944
|
+
sandboxEnv: profile.sandboxConfig.env
|
|
5945
|
+
});
|
|
5946
|
+
await input.agent.tasks.registerExecutorManifest(await preparedRuntime.attestor.registration());
|
|
5947
|
+
const sandbox = {
|
|
5948
|
+
config: profile.sandboxConfig,
|
|
5949
|
+
rootDir: profile.mountPath,
|
|
5950
|
+
path: profile.source
|
|
5951
|
+
};
|
|
5952
|
+
const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
|
|
5953
|
+
const slotIdentity = {
|
|
5954
|
+
agentName: input.agentName,
|
|
5955
|
+
runtimeProfileId: profile.id,
|
|
5956
|
+
runtimeInstanceId: input.runtimeInstanceId
|
|
5957
|
+
};
|
|
5958
|
+
return {
|
|
5959
|
+
profile,
|
|
5960
|
+
preparedRuntime,
|
|
5961
|
+
sandbox,
|
|
5962
|
+
stateDirs,
|
|
5963
|
+
slotIdentity,
|
|
5964
|
+
executionPlans: createExecutionPlanCache({
|
|
5965
|
+
stateDirs,
|
|
5966
|
+
slotIdentity,
|
|
5967
|
+
warmRetentionSec: input.warmRetentionSec,
|
|
5968
|
+
workspacePolicy: {
|
|
5969
|
+
defaultWorkspaceMode: profile.defaultWorkspaceMode,
|
|
5970
|
+
allowedWorkspaceModes: profile.allowedWorkspaceModes
|
|
5971
|
+
},
|
|
5972
|
+
slotRegistry: input.slotRegistry,
|
|
5973
|
+
runtimeSessionStore: input.runtimeSessionStore,
|
|
5974
|
+
sourceAttemptResolver: input.sourceAttemptResolver
|
|
5975
|
+
})
|
|
5976
|
+
};
|
|
5977
|
+
}
|
|
5978
|
+
//#endregion
|
|
5939
5979
|
//#region src/lib/runtime-context.ts
|
|
5940
5980
|
var storage = new AsyncLocalStorage();
|
|
5941
5981
|
function runWithDaemonRuntimeContext(context, callback) {
|
|
@@ -6331,13 +6371,14 @@ function createApiRuntimeSlotStore(args) {
|
|
|
6331
6371
|
sessionPath: input.sessionPath ?? void 0,
|
|
6332
6372
|
slotKey: input.slotKey,
|
|
6333
6373
|
taskType: input.taskType,
|
|
6374
|
+
warmRetentionSec: input.warmRetentionSec,
|
|
6334
6375
|
workspaceId: input.workspaceId ?? void 0,
|
|
6335
6376
|
workspaceKind: input.workspaceKind,
|
|
6336
6377
|
worktreeBranch: input.worktreeBranch ?? void 0,
|
|
6337
6378
|
worktreePath: input.worktreePath ?? void 0
|
|
6338
6379
|
}, { teamId: input.teamId });
|
|
6339
6380
|
},
|
|
6340
|
-
async finishSlot(teamId, taskId, attemptN, identity, slotKey, provider, model, sessionPath) {
|
|
6381
|
+
async finishSlot(teamId, taskId, attemptN, identity, slotKey, provider, model, sessionPath, warmRetentionSec) {
|
|
6341
6382
|
await agent.runtimeSlots.finish({
|
|
6342
6383
|
agentName: identity.agentName,
|
|
6343
6384
|
attemptN,
|
|
@@ -6346,7 +6387,8 @@ function createApiRuntimeSlotStore(args) {
|
|
|
6346
6387
|
provider,
|
|
6347
6388
|
sessionPath: sessionPath ?? void 0,
|
|
6348
6389
|
slotKey,
|
|
6349
|
-
taskId
|
|
6390
|
+
taskId,
|
|
6391
|
+
warmRetentionSec
|
|
6350
6392
|
}, { teamId });
|
|
6351
6393
|
},
|
|
6352
6394
|
async findLatestSlotByTaskAttempt(teamId, taskId, attemptN) {
|
|
@@ -6482,17 +6524,6 @@ function resolveOutputBranch(output) {
|
|
|
6482
6524
|
return typeof branch === "string" && branch.length > 0 ? branch : null;
|
|
6483
6525
|
}
|
|
6484
6526
|
//#endregion
|
|
6485
|
-
//#region src/lib/state-dir.ts
|
|
6486
|
-
function ensureDaemonStateDirs(mountPath) {
|
|
6487
|
-
const rootDir = join(mountPath, ".moltnet", "d");
|
|
6488
|
-
const piSessionsDir = join(rootDir, "pi-sessions");
|
|
6489
|
-
mkdirSync(piSessionsDir, { recursive: true });
|
|
6490
|
-
return {
|
|
6491
|
-
rootDir,
|
|
6492
|
-
piSessionsDir
|
|
6493
|
-
};
|
|
6494
|
-
}
|
|
6495
|
-
//#endregion
|
|
6496
6527
|
//#region src/lib/turn-event-logger.ts
|
|
6497
6528
|
function makeTurnEventHandler(base, context = {}) {
|
|
6498
6529
|
const log = base.child({
|
|
@@ -6523,7 +6554,7 @@ async function runPolling(opts) {
|
|
|
6523
6554
|
const { values } = parseArgs({
|
|
6524
6555
|
args: opts.argv,
|
|
6525
6556
|
options: {
|
|
6526
|
-
...
|
|
6557
|
+
...runtimeCommandOptionDefs(),
|
|
6527
6558
|
team: { type: "string" },
|
|
6528
6559
|
"task-types": { type: "string" },
|
|
6529
6560
|
"correlation-id": { type: "string" },
|
|
@@ -6561,9 +6592,9 @@ async function runPolling(opts) {
|
|
|
6561
6592
|
return 1;
|
|
6562
6593
|
}
|
|
6563
6594
|
const diaryIds = parseCsv(values["diary-ids"]);
|
|
6564
|
-
let
|
|
6595
|
+
let commandOptions;
|
|
6565
6596
|
try {
|
|
6566
|
-
|
|
6597
|
+
commandOptions = parseRuntimeCommandOptions(values);
|
|
6567
6598
|
} catch (err) {
|
|
6568
6599
|
if (err instanceof MissingRequiredOptionError) {
|
|
6569
6600
|
console.error(`${err.message}\n`);
|
|
@@ -6572,6 +6603,7 @@ async function runPolling(opts) {
|
|
|
6572
6603
|
}
|
|
6573
6604
|
throw err;
|
|
6574
6605
|
}
|
|
6606
|
+
const { identity, operations } = commandOptions;
|
|
6575
6607
|
const pollIntervalMs = optionalPositiveInt(values["poll-interval-ms"], "poll-interval-ms", 2e3);
|
|
6576
6608
|
const maxPollIntervalMs = optionalPositiveInt(values["max-poll-interval-ms"], "max-poll-interval-ms", 3e4);
|
|
6577
6609
|
const listLimit = optionalPositiveInt(values["list-limit"], "list-limit", 10);
|
|
@@ -6596,7 +6628,7 @@ async function runPolling(opts) {
|
|
|
6596
6628
|
const { ctx, signingPrivateKey, startupWhoami, agentIdentity, hostCapabilitySigner } = await (async () => {
|
|
6597
6629
|
let gate = "resolve_agent_context";
|
|
6598
6630
|
try {
|
|
6599
|
-
const resolvedContext = await resolveAgentContext(
|
|
6631
|
+
const resolvedContext = await resolveAgentContext(identity.agent, {
|
|
6600
6632
|
agentRootDir: explicitAgentRootDir,
|
|
6601
6633
|
credentialSource: cfg.credentialSource,
|
|
6602
6634
|
envApiUrl: cfg.apiUrl
|
|
@@ -6623,7 +6655,7 @@ async function runPolling(opts) {
|
|
|
6623
6655
|
});
|
|
6624
6656
|
gate = "resolve_agent_identity";
|
|
6625
6657
|
const agentIdentity = await resolveDaemonAgentIdentity({
|
|
6626
|
-
agentName:
|
|
6658
|
+
agentName: identity.agent,
|
|
6627
6659
|
whoami,
|
|
6628
6660
|
credentialSource: cfg.credentialSource,
|
|
6629
6661
|
agentDir: resolvedContext.agentDir,
|
|
@@ -6643,9 +6675,9 @@ async function runPolling(opts) {
|
|
|
6643
6675
|
} catch (error) {
|
|
6644
6676
|
await logDaemonStartupFailure({
|
|
6645
6677
|
serviceName: `agent-daemon.${opts.modeLabel}`,
|
|
6646
|
-
level: cfg.logLevel || (
|
|
6678
|
+
level: cfg.logLevel || (identity.debug ? "debug" : "info"),
|
|
6647
6679
|
gate,
|
|
6648
|
-
agent:
|
|
6680
|
+
agent: identity.agent,
|
|
6649
6681
|
credentialSource: cfg.credentialSource,
|
|
6650
6682
|
error
|
|
6651
6683
|
});
|
|
@@ -6659,23 +6691,43 @@ async function runPolling(opts) {
|
|
|
6659
6691
|
teamId,
|
|
6660
6692
|
cwd: daemonRootDir
|
|
6661
6693
|
});
|
|
6694
|
+
const { logger, shutdown: shutdownLogger } = createRootLogger({
|
|
6695
|
+
name: `agent-daemon.${opts.modeLabel}`,
|
|
6696
|
+
level: cfg.logLevel || (identity.debug ? "debug" : "info")
|
|
6697
|
+
});
|
|
6698
|
+
const rootLogger = logger.child({
|
|
6699
|
+
mode: opts.modeLabel,
|
|
6700
|
+
agent: identity.agent,
|
|
6701
|
+
teamId,
|
|
6702
|
+
runtimeProfileIds: resolvedProfiles.map((profile) => profile.id),
|
|
6703
|
+
runtimeProfileNames: resolvedProfiles.map((profile) => profile.name)
|
|
6704
|
+
});
|
|
6662
6705
|
const runtimeAdapter = opts.runtimeAdapter ?? defaultPiDaemonAdapter;
|
|
6663
|
-
const preparedRuntimes = /* @__PURE__ */ new Map();
|
|
6664
6706
|
const profiles = [];
|
|
6707
|
+
const runtimes = /* @__PURE__ */ new Map();
|
|
6665
6708
|
const skippedProfileBoundaries = [];
|
|
6709
|
+
const slotRegistry = createApiRuntimeSlotStore({ agent: ctx.agent });
|
|
6710
|
+
const runtimeSessionStore = createApiRuntimeSessionStore({
|
|
6711
|
+
agent: ctx.agent,
|
|
6712
|
+
logger: { warn: (context, message) => rootLogger.warn(context, message) }
|
|
6713
|
+
});
|
|
6714
|
+
const sourceAttemptResolver = createApiSourceAttemptResolver({ agent: ctx.agent });
|
|
6715
|
+
const runtimeInstanceId = createRuntimeInstanceId();
|
|
6666
6716
|
for (const profile of resolvedProfiles) try {
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6717
|
+
const prepared = await prepareRuntimeProfile({
|
|
6718
|
+
agent: ctx.agent,
|
|
6719
|
+
agentName: identity.agent,
|
|
6720
|
+
profile,
|
|
6721
|
+
prerequisiteEnv: cfg.profilePrerequisiteEnv,
|
|
6722
|
+
runtimeAdapter,
|
|
6723
|
+
runtimeInstanceId,
|
|
6724
|
+
signingPrivateKey,
|
|
6725
|
+
slotRegistry,
|
|
6726
|
+
runtimeSessionStore,
|
|
6727
|
+
sourceAttemptResolver,
|
|
6728
|
+
warmRetentionSec: operations.warmRetentionSec
|
|
6676
6729
|
});
|
|
6677
|
-
|
|
6678
|
-
preparedRuntimes.set(profile.id, prepared);
|
|
6730
|
+
runtimes.set(profile.id, prepared);
|
|
6679
6731
|
profiles.push(profile);
|
|
6680
6732
|
} catch (error) {
|
|
6681
6733
|
if (!(error instanceof GuestEnvironmentBoundaryError)) throw error;
|
|
@@ -6688,84 +6740,22 @@ async function runPolling(opts) {
|
|
|
6688
6740
|
const details = skippedProfileBoundaries.map(({ profile, error }) => `${profile.name}: ${error.message}`).join("; ");
|
|
6689
6741
|
throw new Error(`No safe runtime profiles remain. ${details}`);
|
|
6690
6742
|
}
|
|
6691
|
-
const slotRegistry = createApiRuntimeSlotStore({ agent: ctx.agent });
|
|
6692
|
-
const runtimeSessionStore = createApiRuntimeSessionStore({
|
|
6693
|
-
agent: ctx.agent,
|
|
6694
|
-
logger: { warn: (context, message) => rootLogger.warn(context, message) }
|
|
6695
|
-
});
|
|
6696
|
-
const sourceAttemptResolver = createApiSourceAttemptResolver({ agent: ctx.agent });
|
|
6697
|
-
const runtimeInstanceId = createRuntimeInstanceId();
|
|
6698
6743
|
const piAgentDir = await resolvePiAgentDir(cfg, profiles[0].mountPath, profiles);
|
|
6699
6744
|
process.once("exit", piAgentDir.cleanup);
|
|
6700
6745
|
activatePiCodingAgentDir(piAgentDir.path, piAgentDir.env);
|
|
6701
|
-
|
|
6702
|
-
for (const profile of profiles) {
|
|
6703
|
-
const common = parseCommonOptions(values, { runtimeDefaults: {
|
|
6704
|
-
leaseTtlSec: profile.leaseTtlSec,
|
|
6705
|
-
heartbeatIntervalMs: profile.heartbeatIntervalMs,
|
|
6706
|
-
maxBatchSize: profile.maxBatchSize,
|
|
6707
|
-
maxTurns: profile.maxTurns,
|
|
6708
|
-
maxBashTimeouts: profile.maxBashTimeouts,
|
|
6709
|
-
warmSessionTtlSec: resolveProfileWarmSessionTtlSec(profile)
|
|
6710
|
-
} });
|
|
6711
|
-
const sandbox = {
|
|
6712
|
-
config: profile.sandboxConfig,
|
|
6713
|
-
rootDir: profile.mountPath,
|
|
6714
|
-
path: profile.source
|
|
6715
|
-
};
|
|
6716
|
-
const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
|
|
6717
|
-
const slotIdentity = {
|
|
6718
|
-
agentName: common.agent,
|
|
6719
|
-
runtimeProfileId: profile.id,
|
|
6720
|
-
runtimeInstanceId
|
|
6721
|
-
};
|
|
6722
|
-
const executionPlans = createExecutionPlanCache({
|
|
6723
|
-
stateDirs,
|
|
6724
|
-
slotIdentity,
|
|
6725
|
-
warmSessionTtlSec: common.warmSessionTtlSec,
|
|
6726
|
-
workspacePolicy: {
|
|
6727
|
-
defaultWorkspaceMode: profile.defaultWorkspaceMode,
|
|
6728
|
-
allowedWorkspaceModes: profile.allowedWorkspaceModes
|
|
6729
|
-
},
|
|
6730
|
-
slotRegistry,
|
|
6731
|
-
runtimeSessionStore,
|
|
6732
|
-
sourceAttemptResolver
|
|
6733
|
-
});
|
|
6734
|
-
runtimes.set(profile.id, {
|
|
6735
|
-
common,
|
|
6736
|
-
profile,
|
|
6737
|
-
sandbox,
|
|
6738
|
-
stateDirs,
|
|
6739
|
-
slotIdentity,
|
|
6740
|
-
executionPlans,
|
|
6741
|
-
preparedRuntime: preparedRuntimes.get(profile.id)
|
|
6742
|
-
});
|
|
6743
|
-
}
|
|
6744
|
-
const firstRuntime = runtimes.get(profiles[0].id);
|
|
6745
|
-
if (!firstRuntime) throw new Error("No runtime profiles resolved");
|
|
6746
|
+
if (!runtimes.has(profiles[0].id)) throw new Error("No runtime profiles resolved");
|
|
6746
6747
|
const otelShutdown = await initWorkerOtel({
|
|
6747
6748
|
serviceName: opts.serviceName,
|
|
6748
6749
|
agent: ctx.agent,
|
|
6749
6750
|
endpoint: cfg.otelEndpoint,
|
|
6750
6751
|
resourceAttributes: {
|
|
6751
6752
|
"moltnet.team.id": teamId,
|
|
6752
|
-
"moltnet.agent.name":
|
|
6753
|
+
"moltnet.agent.name": identity.agent,
|
|
6753
6754
|
"moltnet.credential.source": ctx.credentialSource,
|
|
6754
6755
|
"moltnet.runtime_profile.count": String(profiles.length),
|
|
6755
6756
|
"moltnet.runtime_profile.ids": profiles.map((p) => p.id).join(",")
|
|
6756
6757
|
}
|
|
6757
6758
|
});
|
|
6758
|
-
const { logger, shutdown: shutdownLogger } = createRootLogger({
|
|
6759
|
-
name: `agent-daemon.${opts.modeLabel}`,
|
|
6760
|
-
level: cfg.logLevel || (baseCommon.debug ? "debug" : "info")
|
|
6761
|
-
});
|
|
6762
|
-
const rootLogger = logger.child({
|
|
6763
|
-
mode: opts.modeLabel,
|
|
6764
|
-
agent: baseCommon.agent,
|
|
6765
|
-
teamId,
|
|
6766
|
-
runtimeProfileIds: profiles.map((p) => p.id),
|
|
6767
|
-
runtimeProfileNames: profiles.map((p) => p.name)
|
|
6768
|
-
});
|
|
6769
6759
|
for (const { profile, error } of skippedProfileBoundaries) rootLogger.warn({
|
|
6770
6760
|
runtimeProfileId: profile.id,
|
|
6771
6761
|
runtimeProfileName: profile.name,
|
|
@@ -6821,13 +6811,10 @@ async function runPolling(opts) {
|
|
|
6821
6811
|
topK: profile.topK,
|
|
6822
6812
|
maxOutputTokens: profile.maxOutputTokens,
|
|
6823
6813
|
sandbox: runtime.sandbox.path,
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
warmSessionTtlSec: runtime.common.warmSessionTtlSec,
|
|
6829
|
-
profileSessionTtlSec: profile.sessionTtlSec,
|
|
6830
|
-
profileWorkspaceTtlSec: profile.workspaceTtlSec,
|
|
6814
|
+
heartbeatIntervalMs: operations.heartbeatIntervalMs,
|
|
6815
|
+
maxTurns: profile.maxTurns,
|
|
6816
|
+
maxBashTimeouts: profile.maxBashTimeouts,
|
|
6817
|
+
warmRetentionSec: operations.warmRetentionSec,
|
|
6831
6818
|
defaultWorkspaceMode: profile.defaultWorkspaceMode,
|
|
6832
6819
|
allowedWorkspaceModes: profile.allowedWorkspaceModes
|
|
6833
6820
|
};
|
|
@@ -6852,7 +6839,7 @@ async function runPolling(opts) {
|
|
|
6852
6839
|
runtimeSlotStore: slotRegistry,
|
|
6853
6840
|
taskReader: ctx.agent.tasks
|
|
6854
6841
|
}, {
|
|
6855
|
-
agentName:
|
|
6842
|
+
agentName: identity.agent,
|
|
6856
6843
|
mainWorktree: resolveMainWorktree$1(selected.sandbox.rootDir),
|
|
6857
6844
|
runtimeInstanceId,
|
|
6858
6845
|
runtimeProfileId: profile.id,
|
|
@@ -6885,12 +6872,8 @@ async function runPolling(opts) {
|
|
|
6885
6872
|
teamId,
|
|
6886
6873
|
taskTypes: taskTypes.length > 0 ? taskTypes : void 0,
|
|
6887
6874
|
correlationId: values["correlation-id"],
|
|
6888
|
-
profiles: profiles.map((profile) => ({
|
|
6889
|
-
profileId: profile.id,
|
|
6890
|
-
leaseTtlSec: requireRuntime(runtimes, profile.id).common.leaseTtlSec
|
|
6891
|
-
})),
|
|
6875
|
+
profiles: profiles.map((profile) => ({ profileId: profile.id })),
|
|
6892
6876
|
diaryIds: diaryIds.length > 0 ? diaryIds : void 0,
|
|
6893
|
-
leaseTtlSec: firstRuntime.common.leaseTtlSec,
|
|
6894
6877
|
listLimit,
|
|
6895
6878
|
pollIntervalMs,
|
|
6896
6879
|
maxPollIntervalMs,
|
|
@@ -6898,7 +6881,7 @@ async function runPolling(opts) {
|
|
|
6898
6881
|
stopWhenEmpty: opts.stopWhenEmpty,
|
|
6899
6882
|
waitForFirstTaskMs: waitForFirstTaskSec * 1e3,
|
|
6900
6883
|
waitAfterTaskMs: waitAfterTaskSec * 1e3,
|
|
6901
|
-
debug:
|
|
6884
|
+
debug: identity.debug,
|
|
6902
6885
|
traceIdlePolling: cfg.traceIdlePolling,
|
|
6903
6886
|
logger: rootLogger,
|
|
6904
6887
|
executorFingerprints: Object.fromEntries(profiles.map((profile) => [profile.id, requireRuntime(runtimes, profile.id).preparedRuntime.attestor.fingerprint])),
|
|
@@ -6907,14 +6890,11 @@ async function runPolling(opts) {
|
|
|
6907
6890
|
sourceAttemptResolver
|
|
6908
6891
|
}),
|
|
6909
6892
|
makeReporter: (claimedTask) => {
|
|
6910
|
-
const selected = runtimeForClaimedTask(runtimes, claimedTask);
|
|
6911
6893
|
return new ApiTaskReporter({
|
|
6912
6894
|
tasks: ctx.agent.tasks,
|
|
6913
6895
|
teamId: claimedTask.task.teamId,
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
maxBatchSize: selected.common.maxBatchSize,
|
|
6917
|
-
flushIntervalMs: selected.common.flushIntervalMs
|
|
6896
|
+
heartbeatIntervalMs: operations.heartbeatIntervalMs,
|
|
6897
|
+
logger: rootLogger
|
|
6918
6898
|
});
|
|
6919
6899
|
},
|
|
6920
6900
|
onTaskFinished: async (output, claimedTask) => {
|
|
@@ -6962,7 +6942,7 @@ async function runPolling(opts) {
|
|
|
6962
6942
|
},
|
|
6963
6943
|
executeTask: async (claimedTask, reporter) => {
|
|
6964
6944
|
const selected = runtimeForClaimedTask(runtimes, claimedTask);
|
|
6965
|
-
const {
|
|
6945
|
+
const { executionPlans, profile, sandbox, slotIdentity, stateDirs } = selected;
|
|
6966
6946
|
if (runtimeCredentialConfig) await observeGovernancePlanSafely({
|
|
6967
6947
|
config: runtimeCredentialConfig,
|
|
6968
6948
|
profile,
|
|
@@ -7076,10 +7056,11 @@ async function runPolling(opts) {
|
|
|
7076
7056
|
worktreeBranch: executionPlan.worktreeBranch,
|
|
7077
7057
|
workspaceKind: executionPlan.workspaceKind,
|
|
7078
7058
|
lastTaskId: claimedTask.task.id,
|
|
7079
|
-
lastAttemptN: claimedTask.attemptN
|
|
7059
|
+
lastAttemptN: claimedTask.attemptN,
|
|
7060
|
+
warmRetentionSec: operations.warmRetentionSec
|
|
7080
7061
|
});
|
|
7081
7062
|
const rawExecuteTask = selected.preparedRuntime.createTaskExecutor({
|
|
7082
|
-
agentName:
|
|
7063
|
+
agentName: identity.agent,
|
|
7083
7064
|
moltnetAgent: ctx.agent,
|
|
7084
7065
|
agentIdentity,
|
|
7085
7066
|
hostCapabilitySigner,
|
|
@@ -7109,8 +7090,8 @@ async function runPolling(opts) {
|
|
|
7109
7090
|
makeExecutionPlan: (task) => executionPlans.getOrCreate(task),
|
|
7110
7091
|
makeOnTurnEvent: makeTurnEventHandlerFactory(taskLogger),
|
|
7111
7092
|
toolPolicyLogger: taskLogger,
|
|
7112
|
-
maxTurns:
|
|
7113
|
-
maxBashTimeouts:
|
|
7093
|
+
maxTurns: profile.maxTurns,
|
|
7094
|
+
maxBashTimeouts: profile.maxBashTimeouts
|
|
7114
7095
|
});
|
|
7115
7096
|
try {
|
|
7116
7097
|
active = {
|
|
@@ -7131,7 +7112,7 @@ async function runPolling(opts) {
|
|
|
7131
7112
|
} finally {
|
|
7132
7113
|
active = null;
|
|
7133
7114
|
executionPlans.delete(claimedTask);
|
|
7134
|
-
if (executionPlan.slotKey) await slotRegistry.finishSlot(claimedTask.task.teamId, claimedTask.task.id, claimedTask.attemptN, slotIdentity, executionPlan.slotKey, profile.provider, profile.model, executionPlan.sessionPersistence ? resolveLatestPiSessionPath(executionPlan.sessionPersistence.sessionDir) : null);
|
|
7115
|
+
if (executionPlan.slotKey) await slotRegistry.finishSlot(claimedTask.task.teamId, claimedTask.task.id, claimedTask.attemptN, slotIdentity, executionPlan.slotKey, profile.provider, profile.model, executionPlan.sessionPersistence ? resolveLatestPiSessionPath(executionPlan.sessionPersistence.sessionDir) : null, operations.warmRetentionSec);
|
|
7135
7116
|
}
|
|
7136
7117
|
}
|
|
7137
7118
|
});
|
|
@@ -7207,7 +7188,7 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7207
7188
|
const { values } = parseArgs({
|
|
7208
7189
|
args: argv,
|
|
7209
7190
|
options: {
|
|
7210
|
-
...
|
|
7191
|
+
...runtimeCommandOptionDefs(),
|
|
7211
7192
|
"task-id": {
|
|
7212
7193
|
type: "string",
|
|
7213
7194
|
short: "t"
|
|
@@ -7228,9 +7209,9 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7228
7209
|
console.error(ONCE_HELP);
|
|
7229
7210
|
return 1;
|
|
7230
7211
|
}
|
|
7231
|
-
let
|
|
7212
|
+
let commandOptions;
|
|
7232
7213
|
try {
|
|
7233
|
-
|
|
7214
|
+
commandOptions = parseRuntimeCommandOptions(values);
|
|
7234
7215
|
} catch (err) {
|
|
7235
7216
|
if (err instanceof MissingRequiredOptionError) {
|
|
7236
7217
|
console.error(`${err.message}\n`);
|
|
@@ -7239,6 +7220,7 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7239
7220
|
}
|
|
7240
7221
|
throw err;
|
|
7241
7222
|
}
|
|
7223
|
+
const { identity, operations } = commandOptions;
|
|
7242
7224
|
if (values.sandbox) {
|
|
7243
7225
|
console.error("Cannot use --sandbox. Remote runtime profiles define sandbox policy.");
|
|
7244
7226
|
return 1;
|
|
@@ -7249,12 +7231,11 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7249
7231
|
bindings: cfg.credentialBindings
|
|
7250
7232
|
};
|
|
7251
7233
|
const runtimeCredentialConfig = resolveCredentialEnforcement(cfg.credentialEnforcement, credentialSources) === "off" ? null : loadRuntimeCredentialConfig(credentialSources);
|
|
7252
|
-
const initialOpts = opts;
|
|
7253
7234
|
const explicitAgentRootDir = values["agent-root"] ? resolve(process.cwd(), values["agent-root"]) : void 0;
|
|
7254
7235
|
const { ctx, signingPrivateKey, agentIdentity, hostCapabilitySigner } = await (async () => {
|
|
7255
7236
|
let gate = "resolve_agent_context";
|
|
7256
7237
|
try {
|
|
7257
|
-
const resolvedContext = await resolveAgentContext(
|
|
7238
|
+
const resolvedContext = await resolveAgentContext(identity.agent, {
|
|
7258
7239
|
agentRootDir: explicitAgentRootDir,
|
|
7259
7240
|
credentialSource: cfg.credentialSource,
|
|
7260
7241
|
envApiUrl: cfg.apiUrl
|
|
@@ -7281,7 +7262,7 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7281
7262
|
});
|
|
7282
7263
|
gate = "resolve_agent_identity";
|
|
7283
7264
|
const agentIdentity = await resolveDaemonAgentIdentity({
|
|
7284
|
-
agentName:
|
|
7265
|
+
agentName: identity.agent,
|
|
7285
7266
|
whoami,
|
|
7286
7267
|
credentialSource: cfg.credentialSource,
|
|
7287
7268
|
agentDir: resolvedContext.agentDir,
|
|
@@ -7300,9 +7281,9 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7300
7281
|
} catch (error) {
|
|
7301
7282
|
await logDaemonStartupFailure({
|
|
7302
7283
|
serviceName: "agent-daemon.once",
|
|
7303
|
-
level: cfg.logLevel || (
|
|
7284
|
+
level: cfg.logLevel || (identity.debug ? "debug" : "info"),
|
|
7304
7285
|
gate,
|
|
7305
|
-
agent:
|
|
7286
|
+
agent: identity.agent,
|
|
7306
7287
|
credentialSource: cfg.credentialSource,
|
|
7307
7288
|
error
|
|
7308
7289
|
});
|
|
@@ -7316,34 +7297,23 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7316
7297
|
teamId: values.team,
|
|
7317
7298
|
cwd: daemonRootDir
|
|
7318
7299
|
});
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
tools: preparedRuntime.tools,
|
|
7323
|
-
executables: preparedRuntime.executables
|
|
7300
|
+
const { logger, shutdown: shutdownLogger } = createRootLogger({
|
|
7301
|
+
name: "agent-daemon.once",
|
|
7302
|
+
level: cfg.logLevel || (identity.debug ? "debug" : "info")
|
|
7324
7303
|
});
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7304
|
+
const rootLogger = logger.child({
|
|
7305
|
+
mode: "once",
|
|
7306
|
+
agent: identity.agent,
|
|
7307
|
+
provider: profile.provider,
|
|
7308
|
+
model: profile.model,
|
|
7309
|
+
thinkingLevel: profile.thinkingLevel,
|
|
7310
|
+
temperature: profile.temperature,
|
|
7311
|
+
topP: profile.topP,
|
|
7312
|
+
topK: profile.topK,
|
|
7313
|
+
maxOutputTokens: profile.maxOutputTokens,
|
|
7314
|
+
runtimeProfileId: profile.id,
|
|
7315
|
+
runtimeProfileName: profile.name
|
|
7328
7316
|
});
|
|
7329
|
-
await ctx.agent.tasks.registerExecutorManifest(await preparedRuntime.attestor.registration());
|
|
7330
|
-
opts = parseCommonOptions(values, { runtimeDefaults: {
|
|
7331
|
-
leaseTtlSec: profile.leaseTtlSec,
|
|
7332
|
-
heartbeatIntervalMs: profile.heartbeatIntervalMs,
|
|
7333
|
-
maxBatchSize: profile.maxBatchSize,
|
|
7334
|
-
maxTurns: profile.maxTurns,
|
|
7335
|
-
maxBashTimeouts: profile.maxBashTimeouts,
|
|
7336
|
-
warmSessionTtlSec: resolveProfileWarmSessionTtlSec(profile)
|
|
7337
|
-
} });
|
|
7338
|
-
const sandbox = {
|
|
7339
|
-
config: profile.sandboxConfig,
|
|
7340
|
-
rootDir: profile.mountPath,
|
|
7341
|
-
path: profile.source
|
|
7342
|
-
};
|
|
7343
|
-
const piAgentDir = await resolvePiAgentDir(cfg, sandbox.rootDir, [profile]);
|
|
7344
|
-
process.once("exit", piAgentDir.cleanup);
|
|
7345
|
-
activatePiCodingAgentDir(piAgentDir.path, piAgentDir.env);
|
|
7346
|
-
const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
|
|
7347
7317
|
const slotRegistry = createApiRuntimeSlotStore({ agent: ctx.agent });
|
|
7348
7318
|
const runtimeSessionStore = createApiRuntimeSessionStore({
|
|
7349
7319
|
agent: ctx.agent,
|
|
@@ -7351,30 +7321,29 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7351
7321
|
});
|
|
7352
7322
|
const sourceAttemptResolver = createApiSourceAttemptResolver({ agent: ctx.agent });
|
|
7353
7323
|
const runtimeInstanceId = createRuntimeInstanceId();
|
|
7354
|
-
const slotIdentity = {
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
warmSessionTtlSec: opts.warmSessionTtlSec,
|
|
7363
|
-
workspacePolicy: {
|
|
7364
|
-
defaultWorkspaceMode: profile.defaultWorkspaceMode,
|
|
7365
|
-
allowedWorkspaceModes: profile.allowedWorkspaceModes
|
|
7366
|
-
},
|
|
7324
|
+
const { executionPlans, preparedRuntime, sandbox, slotIdentity, stateDirs } = await prepareRuntimeProfile({
|
|
7325
|
+
agent: ctx.agent,
|
|
7326
|
+
agentName: identity.agent,
|
|
7327
|
+
profile,
|
|
7328
|
+
prerequisiteEnv: cfg.profilePrerequisiteEnv,
|
|
7329
|
+
runtimeAdapter,
|
|
7330
|
+
runtimeInstanceId,
|
|
7331
|
+
signingPrivateKey,
|
|
7367
7332
|
slotRegistry,
|
|
7368
7333
|
runtimeSessionStore,
|
|
7369
|
-
sourceAttemptResolver
|
|
7334
|
+
sourceAttemptResolver,
|
|
7335
|
+
warmRetentionSec: operations.warmRetentionSec
|
|
7370
7336
|
});
|
|
7337
|
+
const piAgentDir = await resolvePiAgentDir(cfg, sandbox.rootDir, [profile]);
|
|
7338
|
+
process.once("exit", piAgentDir.cleanup);
|
|
7339
|
+
activatePiCodingAgentDir(piAgentDir.path, piAgentDir.env);
|
|
7371
7340
|
const otelShutdown = await initWorkerOtel({
|
|
7372
7341
|
serviceName: "moltnet.agent-daemon.once",
|
|
7373
7342
|
agent: ctx.agent,
|
|
7374
7343
|
endpoint: cfg.otelEndpoint,
|
|
7375
7344
|
resourceAttributes: {
|
|
7376
7345
|
"moltnet.task.id": taskId,
|
|
7377
|
-
"moltnet.agent.name":
|
|
7346
|
+
"moltnet.agent.name": identity.agent,
|
|
7378
7347
|
"moltnet.credential.source": ctx.credentialSource,
|
|
7379
7348
|
"moltnet.llm.provider": profile.provider,
|
|
7380
7349
|
"moltnet.llm.model": profile.model,
|
|
@@ -7386,34 +7355,14 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7386
7355
|
"moltnet.runtime_profile.id": profile.id
|
|
7387
7356
|
}
|
|
7388
7357
|
});
|
|
7389
|
-
const { logger, shutdown: shutdownLogger } = createRootLogger({
|
|
7390
|
-
name: "agent-daemon.once",
|
|
7391
|
-
level: cfg.logLevel || (opts.debug ? "debug" : "info")
|
|
7392
|
-
});
|
|
7393
|
-
const rootLogger = logger.child({
|
|
7394
|
-
mode: "once",
|
|
7395
|
-
agent: opts.agent,
|
|
7396
|
-
provider: profile.provider,
|
|
7397
|
-
model: profile.model,
|
|
7398
|
-
thinkingLevel: profile.thinkingLevel,
|
|
7399
|
-
temperature: profile.temperature,
|
|
7400
|
-
topP: profile.topP,
|
|
7401
|
-
topK: profile.topK,
|
|
7402
|
-
maxOutputTokens: profile.maxOutputTokens,
|
|
7403
|
-
runtimeProfileId: profile.id,
|
|
7404
|
-
runtimeProfileName: profile.name
|
|
7405
|
-
});
|
|
7406
7358
|
rootLogger.info({
|
|
7407
7359
|
sandbox: sandbox.path,
|
|
7408
7360
|
taskId,
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
warmSessionTtlSec: opts.warmSessionTtlSec,
|
|
7361
|
+
heartbeatIntervalMs: operations.heartbeatIntervalMs,
|
|
7362
|
+
maxTurns: profile.maxTurns,
|
|
7363
|
+
maxBashTimeouts: profile.maxBashTimeouts,
|
|
7364
|
+
warmRetentionSec: operations.warmRetentionSec,
|
|
7414
7365
|
profileId: profile.id,
|
|
7415
|
-
profileSessionTtlSec: profile.sessionTtlSec,
|
|
7416
|
-
profileWorkspaceTtlSec: profile.workspaceTtlSec,
|
|
7417
7366
|
piAgentDir: piAgentDir.path,
|
|
7418
7367
|
piAgentDirSource: piAgentDir.source
|
|
7419
7368
|
}, "agent-daemon.starting");
|
|
@@ -7425,7 +7374,7 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7425
7374
|
runtimeSlotStore: slotRegistry,
|
|
7426
7375
|
taskReader: ctx.agent.tasks
|
|
7427
7376
|
}, {
|
|
7428
|
-
agentName:
|
|
7377
|
+
agentName: identity.agent,
|
|
7429
7378
|
mainWorktree: resolveMainWorktree(sandbox.rootDir),
|
|
7430
7379
|
runtimeInstanceId,
|
|
7431
7380
|
runtimeProfileId: profile.id,
|
|
@@ -7471,7 +7420,7 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7471
7420
|
});
|
|
7472
7421
|
try {
|
|
7473
7422
|
const rawExecuteTask = preparedRuntime.createTaskExecutor({
|
|
7474
|
-
agentName:
|
|
7423
|
+
agentName: identity.agent,
|
|
7475
7424
|
moltnetAgent: ctx.agent,
|
|
7476
7425
|
agentIdentity,
|
|
7477
7426
|
hostCapabilitySigner,
|
|
@@ -7501,8 +7450,8 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7501
7450
|
makeExecutionPlan: (claimedTask) => executionPlans.getOrCreate(claimedTask),
|
|
7502
7451
|
onTurnEvent: makeTurnEventHandler(rootLogger, { taskId }),
|
|
7503
7452
|
toolPolicyLogger: rootLogger,
|
|
7504
|
-
maxTurns:
|
|
7505
|
-
maxBashTimeouts:
|
|
7453
|
+
maxTurns: profile.maxTurns,
|
|
7454
|
+
maxBashTimeouts: profile.maxBashTimeouts
|
|
7506
7455
|
});
|
|
7507
7456
|
const executeTask = async (claimedTask, reporter) => {
|
|
7508
7457
|
if (runtimeCredentialConfig) await observeGovernancePlanSafely({
|
|
@@ -7559,7 +7508,8 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7559
7508
|
worktreeBranch: executionPlan.worktreeBranch,
|
|
7560
7509
|
workspaceKind: executionPlan.workspaceKind,
|
|
7561
7510
|
lastTaskId: claimedTask.task.id,
|
|
7562
|
-
lastAttemptN: claimedTask.attemptN
|
|
7511
|
+
lastAttemptN: claimedTask.attemptN,
|
|
7512
|
+
warmRetentionSec: operations.warmRetentionSec
|
|
7563
7513
|
});
|
|
7564
7514
|
activeAttemptN = claimedTask.attemptN;
|
|
7565
7515
|
try {
|
|
@@ -7577,7 +7527,7 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7577
7527
|
} finally {
|
|
7578
7528
|
activeAttemptN = null;
|
|
7579
7529
|
executionPlans.delete(claimedTask);
|
|
7580
|
-
if (executionPlan.slotKey) await slotRegistry.finishSlot(claimedTask.task.teamId, claimedTask.task.id, claimedTask.attemptN, slotIdentity, executionPlan.slotKey, profile.provider, profile.model, executionPlan.sessionPersistence ? resolveLatestPiSessionPath(executionPlan.sessionPersistence.sessionDir) : null);
|
|
7530
|
+
if (executionPlan.slotKey) await slotRegistry.finishSlot(claimedTask.task.teamId, claimedTask.task.id, claimedTask.attemptN, slotIdentity, executionPlan.slotKey, profile.provider, profile.model, executionPlan.sessionPersistence ? resolveLatestPiSessionPath(executionPlan.sessionPersistence.sessionDir) : null, operations.warmRetentionSec);
|
|
7581
7531
|
}
|
|
7582
7532
|
};
|
|
7583
7533
|
const writeCorrelationAnchors = makePrBodyAnchorWriter({
|
|
@@ -7590,17 +7540,14 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7590
7540
|
agent: ctx.agent,
|
|
7591
7541
|
taskId,
|
|
7592
7542
|
teamId: profile.teamId,
|
|
7593
|
-
leaseTtlSec: opts.leaseTtlSec,
|
|
7594
7543
|
profileId: profile.id,
|
|
7595
7544
|
executorFingerprint: preparedRuntime.attestor.fingerprint
|
|
7596
7545
|
}),
|
|
7597
7546
|
makeReporter: () => new ApiTaskReporter({
|
|
7598
7547
|
tasks: ctx.agent.tasks,
|
|
7599
7548
|
teamId: profile.teamId,
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
maxBatchSize: opts.maxBatchSize,
|
|
7603
|
-
flushIntervalMs: opts.flushIntervalMs
|
|
7549
|
+
heartbeatIntervalMs: operations.heartbeatIntervalMs,
|
|
7550
|
+
logger: rootLogger
|
|
7604
7551
|
}),
|
|
7605
7552
|
onTaskFinished: async (output, claimedTask) => {
|
|
7606
7553
|
const resolved = await slotRegistry.findLatestSlotByTaskAttempt(claimedTask.task.teamId, claimedTask.task.id, claimedTask.attemptN);
|
|
@@ -7867,14 +7814,41 @@ var AgentServerModelDiscoveryError = class extends Error {
|
|
|
7867
7814
|
this.statusCode = statusCode;
|
|
7868
7815
|
}
|
|
7869
7816
|
};
|
|
7817
|
+
/**
|
|
7818
|
+
* The Ollama capability that means a model accepts image input. Ollama also
|
|
7819
|
+
* reports `completion`, `tools`, `thinking` and `embedding`; none of those map
|
|
7820
|
+
* onto a Pi input modality, so only this one is read.
|
|
7821
|
+
*/
|
|
7822
|
+
var OLLAMA_VISION_CAPABILITY = "vision";
|
|
7823
|
+
/**
|
|
7824
|
+
* Read Ollama's `capabilities` array, when the endpoint supplies one.
|
|
7825
|
+
*
|
|
7826
|
+
* Returns `undefined` when the field is absent — which is meaningfully
|
|
7827
|
+
* different from "present and without vision". A local Ollama returns
|
|
7828
|
+
* capabilities from `/api/tags`; Ollama Cloud does not, and needs a per-model
|
|
7829
|
+
* `/api/show` probe. Only an explicit absence should trigger that probe.
|
|
7830
|
+
*/
|
|
7831
|
+
function readOllamaModalities(value) {
|
|
7832
|
+
if (!isRecord(value) || !Array.isArray(value["capabilities"])) return void 0;
|
|
7833
|
+
return value["capabilities"].includes(OLLAMA_VISION_CAPABILITY) ? ["text", "image"] : [];
|
|
7834
|
+
}
|
|
7870
7835
|
var ModelDiscoveryCollector = class {
|
|
7871
|
-
|
|
7836
|
+
/**
|
|
7837
|
+
* Model id → declared input modalities. `undefined` means the id is known but
|
|
7838
|
+
* its capabilities are not, so it is still a probe candidate; `[]` means the
|
|
7839
|
+
* source answered and the model is text-only.
|
|
7840
|
+
*/
|
|
7841
|
+
models = /* @__PURE__ */ new Map();
|
|
7842
|
+
record(id, input) {
|
|
7843
|
+
if (input === void 0 && this.models.has(id)) return;
|
|
7844
|
+
this.models.set(id, input);
|
|
7845
|
+
}
|
|
7872
7846
|
addOpenAiResponse(value) {
|
|
7873
7847
|
if (!isRecord(value) || !Array.isArray(value["data"])) return;
|
|
7874
7848
|
for (const candidate of value["data"]) {
|
|
7875
7849
|
if (!isRecord(candidate)) continue;
|
|
7876
7850
|
const id = candidate["id"];
|
|
7877
|
-
if (typeof id === "string" && id.length > 0) this.
|
|
7851
|
+
if (typeof id === "string" && id.length > 0) this.record(id, void 0);
|
|
7878
7852
|
}
|
|
7879
7853
|
}
|
|
7880
7854
|
addOllamaResponse(value) {
|
|
@@ -7882,16 +7856,28 @@ var ModelDiscoveryCollector = class {
|
|
|
7882
7856
|
for (const candidate of value["models"]) {
|
|
7883
7857
|
if (!isRecord(candidate)) continue;
|
|
7884
7858
|
const name = candidate["name"];
|
|
7885
|
-
if (typeof name === "string" && name.length > 0) this.
|
|
7859
|
+
if (typeof name === "string" && name.length > 0) this.record(name, readOllamaModalities(candidate));
|
|
7886
7860
|
}
|
|
7887
7861
|
}
|
|
7862
|
+
/** Attach modalities learned after collection, e.g. from an `/api/show` probe. */
|
|
7863
|
+
setModalities(id, input) {
|
|
7864
|
+
if (this.models.has(id)) this.models.set(id, input);
|
|
7865
|
+
}
|
|
7888
7866
|
get size() {
|
|
7889
7867
|
return this.models.size;
|
|
7890
7868
|
}
|
|
7891
7869
|
result(providerId, failures) {
|
|
7892
7870
|
if (this.models.size === 0) throw discoveryFailure(providerId, failures);
|
|
7871
|
+
const ids = [...this.models.keys()].sort().slice(0, 500);
|
|
7893
7872
|
return {
|
|
7894
|
-
models:
|
|
7873
|
+
models: ids.map((id) => {
|
|
7874
|
+
const input = this.models.get(id);
|
|
7875
|
+
return input && input.length > 0 ? {
|
|
7876
|
+
id,
|
|
7877
|
+
input: [...input]
|
|
7878
|
+
} : { id };
|
|
7879
|
+
}),
|
|
7880
|
+
unresolved: ids.filter((id) => this.models.get(id) === void 0),
|
|
7895
7881
|
discoveredCount: this.models.size
|
|
7896
7882
|
};
|
|
7897
7883
|
}
|
|
@@ -7952,6 +7938,12 @@ function safeErrorToken(value) {
|
|
|
7952
7938
|
//#endregion
|
|
7953
7939
|
//#region src/lib/provider-configuration.ts
|
|
7954
7940
|
var DEFAULT_PROVIDER_API = "openai-completions";
|
|
7941
|
+
/**
|
|
7942
|
+
* Concurrent `/api/show` probes. Small on purpose: this runs against a
|
|
7943
|
+
* third-party endpoint on an operator's behalf, and discovery is interactive,
|
|
7944
|
+
* so the cap favours being a polite client over shaving a second.
|
|
7945
|
+
*/
|
|
7946
|
+
var MODALITY_PROBE_CONCURRENCY = 5;
|
|
7955
7947
|
var ProviderConfigurationError = class extends Error {
|
|
7956
7948
|
name = "ProviderConfigurationError";
|
|
7957
7949
|
constructor(code, message, statusCode, options) {
|
|
@@ -7989,7 +7981,7 @@ var ProviderConfigurationService = class {
|
|
|
7989
7981
|
api: input.api ?? previous?.api ?? DEFAULT_PROVIDER_API,
|
|
7990
7982
|
baseUrl,
|
|
7991
7983
|
envName: assertProviderEnvName(providerId, input.envName ?? previous?.envName ?? providerEnvName(providerId)),
|
|
7992
|
-
models:
|
|
7984
|
+
models: (input.models ?? previous?.models ?? []).map(copyProviderModel),
|
|
7993
7985
|
...!input.clearApiKey && previous?.apiKeyRef ? { apiKeyRef: previous.apiKeyRef } : {}
|
|
7994
7986
|
};
|
|
7995
7987
|
const key = `pi-provider/${providerId}`;
|
|
@@ -8090,13 +8082,65 @@ var ProviderConfigurationService = class {
|
|
|
8090
8082
|
providerId,
|
|
8091
8083
|
returnedCount: 500
|
|
8092
8084
|
}, "Provider model discovery result was truncated");
|
|
8093
|
-
if (
|
|
8085
|
+
if (isOllamaProvider(providerId, parsed) && result.unresolved.length > 0) await this.resolveOllamaModalities({
|
|
8086
|
+
collector,
|
|
8087
|
+
headers,
|
|
8088
|
+
ids: result.unresolved,
|
|
8089
|
+
origin: parsed.origin,
|
|
8090
|
+
providerId,
|
|
8091
|
+
signal: options.signal
|
|
8092
|
+
});
|
|
8093
|
+
const resolved = collector.result(providerId, failures);
|
|
8094
|
+
const declared = new Map(provider.models.map((model) => [model.id, model.input]));
|
|
8095
|
+
const models = resolved.models.map((model) => {
|
|
8096
|
+
const override = declared.get(model.id);
|
|
8097
|
+
return override && override.length > 0 ? {
|
|
8098
|
+
id: model.id,
|
|
8099
|
+
input: [...override]
|
|
8100
|
+
} : model;
|
|
8101
|
+
});
|
|
8102
|
+
const detected = models.filter((model) => model.input?.includes("image") && !declared.get(model.id)?.includes("image"));
|
|
8103
|
+
if (detected.length > 0) this.logger.info({
|
|
8104
|
+
code: "agent_server_provider_discovery_modalities_detected",
|
|
8105
|
+
models: detected.map((model) => model.id),
|
|
8106
|
+
providerId
|
|
8107
|
+
}, "Provider models reported image input support");
|
|
8108
|
+
if (options.save) await this.set(providerId, { models }, options);
|
|
8094
8109
|
this.logger.info({
|
|
8095
8110
|
code: "agent_server_provider_discovery_completed",
|
|
8096
|
-
modelCount:
|
|
8111
|
+
modelCount: models.length,
|
|
8097
8112
|
providerId
|
|
8098
8113
|
}, "Provider model discovery completed");
|
|
8099
|
-
return { models
|
|
8114
|
+
return { models };
|
|
8115
|
+
}
|
|
8116
|
+
/**
|
|
8117
|
+
* Fill in modalities Ollama Cloud's `/api/tags` omits, one `/api/show` per
|
|
8118
|
+
* still-unknown model.
|
|
8119
|
+
*
|
|
8120
|
+
* Failures here are deliberately not pushed into the discovery `failures`
|
|
8121
|
+
* array: that array decides the error code of a *failed* discovery, so a
|
|
8122
|
+
* probe rejection must not relabel an otherwise-successful one. A model whose
|
|
8123
|
+
* probe fails simply stays text-only.
|
|
8124
|
+
*/
|
|
8125
|
+
async resolveOllamaModalities(input) {
|
|
8126
|
+
const url = `${input.origin}/api/show`;
|
|
8127
|
+
for (let index = 0; index < input.ids.length; index += MODALITY_PROBE_CONCURRENCY) {
|
|
8128
|
+
if (input.signal?.aborted) throw new ProviderConfigurationError("operation_aborted", `provider "${input.providerId}" discovery was cancelled`, 408, { cause: input.signal.reason });
|
|
8129
|
+
const batch = input.ids.slice(index, index + MODALITY_PROBE_CONCURRENCY);
|
|
8130
|
+
await Promise.all(batch.map(async (id) => {
|
|
8131
|
+
const modalities = readOllamaModalities(await this.requestDiscoveryEndpoint({
|
|
8132
|
+
body: { model: id },
|
|
8133
|
+
endpoint: "ollama_show",
|
|
8134
|
+
failures: [],
|
|
8135
|
+
headers: input.headers,
|
|
8136
|
+
method: "POST",
|
|
8137
|
+
providerId: input.providerId,
|
|
8138
|
+
signal: input.signal,
|
|
8139
|
+
url
|
|
8140
|
+
}));
|
|
8141
|
+
if (modalities) input.collector.setModalities(id, modalities);
|
|
8142
|
+
}));
|
|
8143
|
+
}
|
|
8100
8144
|
}
|
|
8101
8145
|
async resolveApiKey(providerId, provider) {
|
|
8102
8146
|
if (!provider.apiKeyRef) return void 0;
|
|
@@ -8117,7 +8161,14 @@ var ProviderConfigurationService = class {
|
|
|
8117
8161
|
let response;
|
|
8118
8162
|
try {
|
|
8119
8163
|
response = await this.fetchImpl(input.url, {
|
|
8120
|
-
|
|
8164
|
+
...input.body ? {
|
|
8165
|
+
body: JSON.stringify(input.body),
|
|
8166
|
+
method: input.method
|
|
8167
|
+
} : {},
|
|
8168
|
+
headers: input.body ? {
|
|
8169
|
+
...input.headers,
|
|
8170
|
+
"content-type": "application/json"
|
|
8171
|
+
} : input.headers,
|
|
8121
8172
|
redirect: "error",
|
|
8122
8173
|
signal: input.signal ? AbortSignal.any([input.signal, timeout]) : timeout
|
|
8123
8174
|
});
|
|
@@ -8184,7 +8235,7 @@ function providerView(provider) {
|
|
|
8184
8235
|
api: provider.api,
|
|
8185
8236
|
baseUrl: provider.baseUrl,
|
|
8186
8237
|
envName: provider.envName,
|
|
8187
|
-
models:
|
|
8238
|
+
models: provider.models.map(copyProviderModel),
|
|
8188
8239
|
hasApiKey: Boolean(provider.apiKeyRef)
|
|
8189
8240
|
};
|
|
8190
8241
|
}
|
|
@@ -8206,6 +8257,31 @@ function providerAbortSource(reason) {
|
|
|
8206
8257
|
}
|
|
8207
8258
|
//#endregion
|
|
8208
8259
|
//#region src/cli/providers.ts
|
|
8260
|
+
var MODEL_MODALITIES$1 = PI_MODEL_MODALITIES;
|
|
8261
|
+
/**
|
|
8262
|
+
* Build the model list from `--model <id>` (text-only) and
|
|
8263
|
+
* `--model-input <id>=text,image` (declares modalities). Model ids contain
|
|
8264
|
+
* colons, so `=` separates the id from its modality list. A `--model-input`
|
|
8265
|
+
* entry also declares the model, and overrides a bare `--model` for that id.
|
|
8266
|
+
*/
|
|
8267
|
+
function parseModelArgs(models, modelInputs) {
|
|
8268
|
+
if (!models && !modelInputs) return void 0;
|
|
8269
|
+
const entries = /* @__PURE__ */ new Map();
|
|
8270
|
+
for (const id of models ?? []) entries.set(id, { id });
|
|
8271
|
+
for (const raw of modelInputs ?? []) {
|
|
8272
|
+
const separator = raw.indexOf("=");
|
|
8273
|
+
if (separator <= 0) throw new ProviderCliError("invalid_arguments", `--model-input expects <model-id>=<modality>[,<modality>], received "${raw}"`);
|
|
8274
|
+
const id = raw.slice(0, separator);
|
|
8275
|
+
const input = raw.slice(separator + 1).split(",").map((value) => value.trim()).filter((value) => value.length > 0);
|
|
8276
|
+
if (input.length === 0) throw new ProviderCliError("invalid_arguments", `--model-input for "${id}" declared no modality`);
|
|
8277
|
+
for (const modality of input) if (!MODEL_MODALITIES$1.includes(modality)) throw new ProviderCliError("invalid_arguments", `--model-input for "${id}" has unknown modality "${modality}"; expected ${MODEL_MODALITIES$1.join(" or ")}`);
|
|
8278
|
+
entries.set(id, {
|
|
8279
|
+
id,
|
|
8280
|
+
input
|
|
8281
|
+
});
|
|
8282
|
+
}
|
|
8283
|
+
return [...entries.values()];
|
|
8284
|
+
}
|
|
8209
8285
|
async function runProviders(argv, dependencies = {}) {
|
|
8210
8286
|
if (isHelpFlag(argv) || argv.length === 0) {
|
|
8211
8287
|
(dependencies.stdout ?? console.log)(PROVIDERS_HELP);
|
|
@@ -8279,6 +8355,10 @@ function parseProviderArgs(command, args) {
|
|
|
8279
8355
|
type: "string",
|
|
8280
8356
|
multiple: true
|
|
8281
8357
|
},
|
|
8358
|
+
"model-input": {
|
|
8359
|
+
type: "string",
|
|
8360
|
+
multiple: true
|
|
8361
|
+
},
|
|
8282
8362
|
"clear-models": { type: "boolean" },
|
|
8283
8363
|
"api-key-stdin": { type: "boolean" },
|
|
8284
8364
|
"clear-api-key": { type: "boolean" }
|
|
@@ -8288,6 +8368,7 @@ function parseProviderArgs(command, args) {
|
|
|
8288
8368
|
});
|
|
8289
8369
|
requirePositionals(positionals, 1, "providers set <id>");
|
|
8290
8370
|
if (values.model && values["clear-models"]) throw new ProviderCliError("invalid_arguments", "--model and --clear-models cannot be used together");
|
|
8371
|
+
if (values["model-input"] && values["clear-models"]) throw new ProviderCliError("invalid_arguments", "--model-input and --clear-models cannot be used together");
|
|
8291
8372
|
if (values["api-key-stdin"] && values["clear-api-key"]) throw new ProviderCliError("invalid_arguments", "--api-key-stdin and --clear-api-key cannot be used together");
|
|
8292
8373
|
return {
|
|
8293
8374
|
command,
|
|
@@ -8295,7 +8376,7 @@ function parseProviderArgs(command, args) {
|
|
|
8295
8376
|
providerId: positionals[0],
|
|
8296
8377
|
baseUrl: values["base-url"],
|
|
8297
8378
|
api: values.api,
|
|
8298
|
-
models: values["clear-models"] ? [] : values.model,
|
|
8379
|
+
models: values["clear-models"] ? [] : parseModelArgs(values.model, values["model-input"]),
|
|
8299
8380
|
apiKeyStdin: values["api-key-stdin"] ?? false,
|
|
8300
8381
|
clearApiKey: values["clear-api-key"] ?? false
|
|
8301
8382
|
};
|
|
@@ -8450,7 +8531,7 @@ async function discoverProvider(context, parsed) {
|
|
|
8450
8531
|
signal: context.signal
|
|
8451
8532
|
});
|
|
8452
8533
|
if (parsed.json) context.stdout(JSON.stringify(result));
|
|
8453
|
-
else for (const model of result.models) context.stdout(model);
|
|
8534
|
+
else for (const model of result.models) context.stdout(model.input && model.input.length > 0 ? `${model.id}\t${model.input.join(",")}` : model.id);
|
|
8454
8535
|
return 0;
|
|
8455
8536
|
}
|
|
8456
8537
|
async function removeProvider(context, parsed) {
|
|
@@ -9842,6 +9923,7 @@ var RunManager = class {
|
|
|
9842
9923
|
extraArgs: ["--agent-root", agentRoot]
|
|
9843
9924
|
};
|
|
9844
9925
|
})();
|
|
9926
|
+
const runtimeSettings = this.options.runtimeSettings ?? DEFAULT_LOCAL_OPERATIONAL_SETTINGS;
|
|
9845
9927
|
const args = [
|
|
9846
9928
|
...runtimeModule ? ["--runtime", runtimeModule] : [],
|
|
9847
9929
|
spec.mode,
|
|
@@ -9852,6 +9934,10 @@ var RunManager = class {
|
|
|
9852
9934
|
...spec.profiles.flatMap((profile) => ["--profile", profile]),
|
|
9853
9935
|
"--task-types",
|
|
9854
9936
|
spec.taskTypes.join(","),
|
|
9937
|
+
"--heartbeat-interval-ms",
|
|
9938
|
+
String(runtimeSettings.heartbeatIntervalMs),
|
|
9939
|
+
"--warm-retention-sec",
|
|
9940
|
+
String(runtimeSettings.warmRetentionSec),
|
|
9855
9941
|
...target.extraArgs
|
|
9856
9942
|
];
|
|
9857
9943
|
if (activation.source === "managed") {
|
|
@@ -10442,6 +10528,13 @@ function writeRegistry(path, entries) {
|
|
|
10442
10528
|
//#region src/lib/agent-server/protocol.ts
|
|
10443
10529
|
var DateTime = Type.String({ format: "date-time" });
|
|
10444
10530
|
var StringList = Type.Array(Type.String());
|
|
10531
|
+
/** A model the provider offers, with the input modalities it accepts. */
|
|
10532
|
+
var ProviderModelSchema = Type.Object({
|
|
10533
|
+
id: Type.String(),
|
|
10534
|
+
input: Type.Optional(Type.Array(Type.Union(PI_MODEL_MODALITIES.map((modality) => Type.Literal(modality))), { minItems: 1 }))
|
|
10535
|
+
});
|
|
10536
|
+
/** One shape on the wire, for both requests and responses. */
|
|
10537
|
+
var ProviderModelList = Type.Array(ProviderModelSchema);
|
|
10445
10538
|
function schemaRef(schema) {
|
|
10446
10539
|
const id = schema.$id;
|
|
10447
10540
|
if (typeof id !== "string" || id.length === 0) throw new Error("Agent Server protocol schemas must have an identifier");
|
|
@@ -10474,7 +10567,7 @@ var AgentServerProviderSchema = Type.Object({
|
|
|
10474
10567
|
api: Type.String(),
|
|
10475
10568
|
baseUrl: Type.String({ format: "uri" }),
|
|
10476
10569
|
envName: Type.String(),
|
|
10477
|
-
models:
|
|
10570
|
+
models: ProviderModelList,
|
|
10478
10571
|
hasApiKey: Type.Boolean()
|
|
10479
10572
|
}, { $id: "AgentServerProvider" });
|
|
10480
10573
|
var AgentServerRunRecordSchema = Type.Object({
|
|
@@ -10522,7 +10615,11 @@ var AgentServerStatusSchema = Type.Object({
|
|
|
10522
10615
|
identities: Type.Array(schemaRef(AgentServerIdentitySchema)),
|
|
10523
10616
|
selectedIdentity: Type.Optional(Type.String()),
|
|
10524
10617
|
providers: Type.Record(Type.String(), schemaRef(AgentServerProviderSchema)),
|
|
10525
|
-
runs: Type.Array(schemaRef(AgentServerRunSchema))
|
|
10618
|
+
runs: Type.Array(schemaRef(AgentServerRunSchema)),
|
|
10619
|
+
runtimeSettings: Type.Object({
|
|
10620
|
+
heartbeatIntervalMs: Type.Integer({ minimum: 0 }),
|
|
10621
|
+
warmRetentionSec: Type.Integer({ minimum: 0 })
|
|
10622
|
+
})
|
|
10526
10623
|
}, { $id: "AgentServerStatus" });
|
|
10527
10624
|
var PairingStartedSchema = Type.Object({
|
|
10528
10625
|
pairingId: Type.String(),
|
|
@@ -10547,10 +10644,10 @@ var PutProviderSchema = Type.Object({
|
|
|
10547
10644
|
api: Type.String(),
|
|
10548
10645
|
baseUrl: Type.String({ format: "uri" }),
|
|
10549
10646
|
envName: Type.String(),
|
|
10550
|
-
models:
|
|
10647
|
+
models: ProviderModelList,
|
|
10551
10648
|
apiKey: Type.Optional(Type.String())
|
|
10552
10649
|
});
|
|
10553
|
-
var DiscoverModelsSchema = Type.Object({ models:
|
|
10650
|
+
var DiscoverModelsSchema = Type.Object({ models: ProviderModelList }, { $id: "DiscoveredModels" });
|
|
10554
10651
|
var StartRunSchema = Type.Object({
|
|
10555
10652
|
agent: Type.String(),
|
|
10556
10653
|
teamId: Type.String(),
|
|
@@ -10865,6 +10962,27 @@ function stringArray(body, field, options = {}) {
|
|
|
10865
10962
|
if (!Array.isArray(value) || !options.allowEmpty && value.length === 0 || value.some((item) => typeof item !== "string" || item.length === 0)) throw new AgentServerHttpError(400, "invalid_body", `"${field}" must be ${options.allowEmpty ? "a" : "a non-empty"} string array`);
|
|
10866
10963
|
return value;
|
|
10867
10964
|
}
|
|
10965
|
+
var MODEL_MODALITIES = new Set(PI_MODEL_MODALITIES);
|
|
10966
|
+
/** Parse the provider `models` field: `{ id, input? }` entries only. */
|
|
10967
|
+
function modelArray(body, field) {
|
|
10968
|
+
const value = body[field];
|
|
10969
|
+
const invalid = (detail) => {
|
|
10970
|
+
throw new AgentServerHttpError(400, "invalid_body", detail);
|
|
10971
|
+
};
|
|
10972
|
+
if (!Array.isArray(value)) return invalid(`"${field}" must be an array of { id, input? } entries`);
|
|
10973
|
+
return value.map((item) => {
|
|
10974
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) return invalid(`"${field}" entries must be an { id, input? } object`);
|
|
10975
|
+
const entry = item;
|
|
10976
|
+
const id = entry.id;
|
|
10977
|
+
if (typeof id !== "string" || id.length === 0) return invalid(`"${field}" entries must carry a non-empty "id"`);
|
|
10978
|
+
if (entry.input === void 0) return { id };
|
|
10979
|
+
if (!Array.isArray(entry.input) || entry.input.length === 0 || entry.input.some((modality) => typeof modality !== "string" || !MODEL_MODALITIES.has(modality))) return invalid(`"${field}" entry "${id}" must declare "input" as a non-empty array of "text" or "image"`);
|
|
10980
|
+
return {
|
|
10981
|
+
id,
|
|
10982
|
+
input: entry.input
|
|
10983
|
+
};
|
|
10984
|
+
});
|
|
10985
|
+
}
|
|
10868
10986
|
function requestOperationSignal(request, shutdownSignal) {
|
|
10869
10987
|
const disconnected = new AbortController();
|
|
10870
10988
|
if (request.raw.aborted) disconnected.abort({ source: "request" });
|
|
@@ -10996,7 +11114,8 @@ function registerStatusRoute(app, options, requirePairedOrigin) {
|
|
|
10996
11114
|
identities: identityViews(store),
|
|
10997
11115
|
...selected ? { selectedIdentity: selected } : {},
|
|
10998
11116
|
providers: options.providers.list(),
|
|
10999
|
-
runs: runViews(runs)
|
|
11117
|
+
runs: runViews(runs),
|
|
11118
|
+
runtimeSettings: options.runtimeSettings ?? DEFAULT_LOCAL_OPERATIONAL_SETTINGS
|
|
11000
11119
|
};
|
|
11001
11120
|
});
|
|
11002
11121
|
}
|
|
@@ -11084,7 +11203,7 @@ function registerProviderRoutes(app, options, requirePairedOrigin) {
|
|
|
11084
11203
|
api: requireString(body, "api"),
|
|
11085
11204
|
baseUrl: requireString(body, "baseUrl"),
|
|
11086
11205
|
envName: requireString(body, "envName"),
|
|
11087
|
-
models:
|
|
11206
|
+
models: modelArray(body, "models"),
|
|
11088
11207
|
...optionalString(body, "apiKey") ? { apiKey: optionalString(body, "apiKey") } : {}
|
|
11089
11208
|
});
|
|
11090
11209
|
return reply.code(200).send(entry);
|
|
@@ -11489,6 +11608,7 @@ async function runAgentServer(argv) {
|
|
|
11489
11608
|
const trustRequested = argv[0] === "trust";
|
|
11490
11609
|
const commandArgs = trustRequested ? argv.slice(1) : argv;
|
|
11491
11610
|
const envConfig = loadAgentServerEnvConfig();
|
|
11611
|
+
if (trustRequested) return runTrustCommand(commandArgs, resolveAgentServerRoot({ root: envConfig.root }));
|
|
11492
11612
|
const { values } = parseArgs({
|
|
11493
11613
|
args: commandArgs,
|
|
11494
11614
|
options: {
|
|
@@ -11496,7 +11616,9 @@ async function runAgentServer(argv) {
|
|
|
11496
11616
|
"allowed-origins": { type: "string" },
|
|
11497
11617
|
root: { type: "string" },
|
|
11498
11618
|
"api-url": { type: "string" },
|
|
11499
|
-
|
|
11619
|
+
"heartbeat-interval-ms": { type: "string" },
|
|
11620
|
+
"warm-retention-sec": { type: "string" },
|
|
11621
|
+
supervised: { type: "boolean" }
|
|
11500
11622
|
}
|
|
11501
11623
|
});
|
|
11502
11624
|
const port = Number.parseInt(values.port ?? (envConfig.port || `${DEFAULT_PORT}`), 10);
|
|
@@ -11507,8 +11629,8 @@ async function runAgentServer(argv) {
|
|
|
11507
11629
|
const allowedOrigins = parseAllowedOrigins(values["allowed-origins"] ?? (envConfig.allowedOrigins || DEFAULT_ALLOWED_ORIGINS));
|
|
11508
11630
|
const root = values.root ?? resolveAgentServerRoot({ root: envConfig.root });
|
|
11509
11631
|
const defaultApiUrl = values["api-url"] ?? (envConfig.apiUrl || DEFAULT_API_URL);
|
|
11632
|
+
const runtimeSettings = parseLocalOperationalSettings(values);
|
|
11510
11633
|
const store = new AgentServerStore(root).ensure();
|
|
11511
|
-
if (trustRequested) return runTrustCommand(commandArgs, root);
|
|
11512
11634
|
const { logger, shutdown: shutdownLogger } = createRootLogger({
|
|
11513
11635
|
name: "agent-daemon.server",
|
|
11514
11636
|
level: envConfig.logLevel || "info"
|
|
@@ -11540,7 +11662,8 @@ async function runAgentServer(argv) {
|
|
|
11540
11662
|
externalSecretProviders,
|
|
11541
11663
|
baseEnv: processEnvSnapshot(),
|
|
11542
11664
|
logger,
|
|
11543
|
-
runtimeRegistry: new RuntimeRegistry(store.root)
|
|
11665
|
+
runtimeRegistry: new RuntimeRegistry(store.root),
|
|
11666
|
+
runtimeSettings
|
|
11544
11667
|
});
|
|
11545
11668
|
const tls = isMacos() ? await ensureTrustedLocalTls(root) : void 0;
|
|
11546
11669
|
const app = buildAgentServer({
|
|
@@ -11559,6 +11682,7 @@ async function runAgentServer(argv) {
|
|
|
11559
11682
|
cert: tls.cert
|
|
11560
11683
|
} } : {},
|
|
11561
11684
|
defaultApiUrl,
|
|
11685
|
+
runtimeSettings,
|
|
11562
11686
|
...envConfig.activeIdentity ? { activeIdentity: envConfig.activeIdentity } : {},
|
|
11563
11687
|
version: "dev",
|
|
11564
11688
|
logger,
|
|
@@ -11573,7 +11697,7 @@ async function runAgentServer(argv) {
|
|
|
11573
11697
|
console.error(`config root: ${root}`);
|
|
11574
11698
|
console.error(`allowed origins: ${allowedOrigins.join(", ")}`);
|
|
11575
11699
|
console.error("Pair from the Console \"Local runtime\" page; approve the one-click prompt this server opens.");
|
|
11576
|
-
return await waitForAgentServerShutdown(runs, app, shutdownController);
|
|
11700
|
+
return await waitForAgentServerShutdown(runs, app, shutdownController, Boolean(values.supervised));
|
|
11577
11701
|
} catch (cause) {
|
|
11578
11702
|
await app.close().catch(() => void 0);
|
|
11579
11703
|
throw cause;
|
|
@@ -11594,19 +11718,80 @@ async function runAgentServer(argv) {
|
|
|
11594
11718
|
await shutdownLogger();
|
|
11595
11719
|
}
|
|
11596
11720
|
}
|
|
11597
|
-
async function runTrustCommand(argv,
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11721
|
+
async function runTrustCommand(argv, defaultRoot) {
|
|
11722
|
+
try {
|
|
11723
|
+
const { values } = parseArgs({
|
|
11724
|
+
args: argv,
|
|
11725
|
+
options: {
|
|
11726
|
+
root: { type: "string" },
|
|
11727
|
+
remove: { type: "boolean" },
|
|
11728
|
+
status: { type: "boolean" },
|
|
11729
|
+
yes: { type: "boolean" },
|
|
11730
|
+
json: { type: "boolean" }
|
|
11731
|
+
}
|
|
11732
|
+
});
|
|
11733
|
+
const root = values.root ?? defaultRoot;
|
|
11734
|
+
const statusRequested = Boolean(values.status);
|
|
11735
|
+
const removeRequested = Boolean(values.remove);
|
|
11736
|
+
const yes = Boolean(values.yes);
|
|
11737
|
+
const json = Boolean(values.json);
|
|
11738
|
+
if (statusRequested && (removeRequested || yes)) {
|
|
11739
|
+
console.error("Usage: moltnet-agent server trust --status [--json]");
|
|
11740
|
+
return 1;
|
|
11741
|
+
}
|
|
11742
|
+
if (!isMacos()) {
|
|
11743
|
+
if (json) {
|
|
11744
|
+
printTrustStatus({
|
|
11745
|
+
supported: false,
|
|
11746
|
+
trusted: false,
|
|
11747
|
+
fingerprint: null
|
|
11748
|
+
});
|
|
11749
|
+
return 0;
|
|
11750
|
+
}
|
|
11751
|
+
console.error("Local HTTPS trust setup is currently supported on macOS only.");
|
|
11752
|
+
return 1;
|
|
11753
|
+
}
|
|
11754
|
+
const material = await ensureLocalTlsMaterial(root);
|
|
11755
|
+
if (statusRequested) {
|
|
11756
|
+
const trusted = await isLocalCaTrusted(root);
|
|
11757
|
+
if (json) printTrustStatus({
|
|
11758
|
+
supported: true,
|
|
11759
|
+
trusted,
|
|
11760
|
+
fingerprint: material.fingerprint
|
|
11761
|
+
});
|
|
11762
|
+
else console.log(trusted ? `MoltNet local CA ${material.fingerprint} is trusted.` : `MoltNet local CA ${material.fingerprint} is not trusted.`);
|
|
11763
|
+
return 0;
|
|
11764
|
+
}
|
|
11765
|
+
if (json && !yes) {
|
|
11766
|
+
console.error("Machine-readable trust changes require --yes after native app consent.");
|
|
11767
|
+
return 1;
|
|
11768
|
+
}
|
|
11769
|
+
if (removeRequested) {
|
|
11770
|
+
await removeLocalCa(root);
|
|
11771
|
+
if (json) printTrustStatus({
|
|
11772
|
+
supported: true,
|
|
11773
|
+
trusted: false,
|
|
11774
|
+
fingerprint: material.fingerprint
|
|
11775
|
+
});
|
|
11776
|
+
else console.log("Removed the MoltNet local CA from your login keychain.");
|
|
11777
|
+
return 0;
|
|
11778
|
+
}
|
|
11779
|
+
if (yes) await trustLocalCa(root);
|
|
11780
|
+
else await ensureTrustedLocalTls(root);
|
|
11781
|
+
if (json) printTrustStatus({
|
|
11782
|
+
supported: true,
|
|
11783
|
+
trusted: await isLocalCaTrusted(root),
|
|
11784
|
+
fingerprint: material.fingerprint
|
|
11785
|
+
});
|
|
11786
|
+
else console.log("MoltNet local HTTPS trust is ready for this macOS user.");
|
|
11605
11787
|
return 0;
|
|
11788
|
+
} catch (cause) {
|
|
11789
|
+
console.error(`Agent Server trust command failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
11790
|
+
return 1;
|
|
11606
11791
|
}
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11792
|
+
}
|
|
11793
|
+
function printTrustStatus(status) {
|
|
11794
|
+
console.log(JSON.stringify(status));
|
|
11610
11795
|
}
|
|
11611
11796
|
async function ensureTrustedLocalTls(root) {
|
|
11612
11797
|
const material = await ensureLocalTlsMaterial(root);
|
|
@@ -11625,12 +11810,13 @@ async function ensureTrustedLocalTls(root) {
|
|
|
11625
11810
|
await trustLocalCa(root);
|
|
11626
11811
|
return material;
|
|
11627
11812
|
}
|
|
11628
|
-
function waitForAgentServerShutdown(runs, app, shutdownController) {
|
|
11813
|
+
function waitForAgentServerShutdown(runs, app, shutdownController, supervised) {
|
|
11629
11814
|
return new Promise((resolvePromise) => {
|
|
11630
11815
|
let shuttingDown = false;
|
|
11631
|
-
const shutdown = () => {
|
|
11816
|
+
const shutdown = (source) => {
|
|
11632
11817
|
if (shuttingDown) return;
|
|
11633
11818
|
shuttingDown = true;
|
|
11819
|
+
if (source === "stdin") console.error("shutting down: stdin EOF");
|
|
11634
11820
|
shutdownController.abort({ source: "shutdown" });
|
|
11635
11821
|
(async () => {
|
|
11636
11822
|
app.server.closeAllConnections();
|
|
@@ -11656,16 +11842,31 @@ function waitForAgentServerShutdown(runs, app, shutdownController) {
|
|
|
11656
11842
|
const failures = results.filter((result) => result.status === "rejected");
|
|
11657
11843
|
for (const failure of failures) console.error(`shutdown cleanup failed: ${failure.reason.message}`);
|
|
11658
11844
|
handlers.dispose();
|
|
11845
|
+
stdinGuard.dispose();
|
|
11659
11846
|
const exitCode = typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
11660
11847
|
resolvePromise(failures.length > 0 ? 1 : exitCode);
|
|
11661
11848
|
})();
|
|
11662
11849
|
};
|
|
11663
11850
|
const handlers = installShutdownSignalHandlers({
|
|
11664
11851
|
logDrain: () => console.error("shutting down: stopping runs…"),
|
|
11665
|
-
drain: shutdown
|
|
11852
|
+
drain: () => shutdown()
|
|
11853
|
+
});
|
|
11854
|
+
const stdinGuard = installSupervisedStdinGuard({
|
|
11855
|
+
enabled: supervised,
|
|
11856
|
+
shutdown: () => shutdown("stdin")
|
|
11666
11857
|
});
|
|
11667
11858
|
});
|
|
11668
11859
|
}
|
|
11860
|
+
/** Makes the supervising process's stdin pipe part of the server lifecycle. */
|
|
11861
|
+
function installSupervisedStdinGuard(options) {
|
|
11862
|
+
if (!options.enabled) return { dispose: () => void 0 };
|
|
11863
|
+
const input = options.input ?? process.stdin;
|
|
11864
|
+
const onEnd = () => options.shutdown();
|
|
11865
|
+
input.once("end", onEnd);
|
|
11866
|
+
input.resume();
|
|
11867
|
+
if (input.readableEnded) queueMicrotask(onEnd);
|
|
11868
|
+
return { dispose: () => input.off("end", onEnd) };
|
|
11869
|
+
}
|
|
11669
11870
|
//#endregion
|
|
11670
11871
|
//#region src/lib/runtime-session-sync.ts
|
|
11671
11872
|
async function syncRuntimeSessions(deps, input) {
|
|
@@ -11782,7 +11983,7 @@ async function runSyncSessions(argv) {
|
|
|
11782
11983
|
const { values } = parseArgs({
|
|
11783
11984
|
args: argv,
|
|
11784
11985
|
options: {
|
|
11785
|
-
...
|
|
11986
|
+
...identityOptionDefs(),
|
|
11786
11987
|
team: { type: "string" },
|
|
11787
11988
|
"runtime-profile-id": { type: "string" },
|
|
11788
11989
|
state: { type: "string" },
|
|
@@ -11795,9 +11996,9 @@ async function runSyncSessions(argv) {
|
|
|
11795
11996
|
console.error(SYNC_SESSIONS_HELP);
|
|
11796
11997
|
return 1;
|
|
11797
11998
|
}
|
|
11798
|
-
let
|
|
11999
|
+
let identity;
|
|
11799
12000
|
try {
|
|
11800
|
-
|
|
12001
|
+
identity = parseIdentityProcessOptions(values);
|
|
11801
12002
|
} catch (err) {
|
|
11802
12003
|
if (err instanceof MissingRequiredOptionError) {
|
|
11803
12004
|
console.error(`${err.message}\n`);
|
|
@@ -11811,7 +12012,7 @@ async function runSyncSessions(argv) {
|
|
|
11811
12012
|
const agentRootDir = resolve(process.cwd(), values["agent-root"] ?? process.cwd());
|
|
11812
12013
|
const explicitAgentRootDir = values["agent-root"] ? resolve(process.cwd(), values["agent-root"]) : void 0;
|
|
11813
12014
|
const cfg = loadConfig();
|
|
11814
|
-
const ctx = await resolveAgentContext(
|
|
12015
|
+
const ctx = await resolveAgentContext(identity.agent, {
|
|
11815
12016
|
credentialSource: cfg.credentialSource,
|
|
11816
12017
|
envApiUrl: cfg.apiUrl,
|
|
11817
12018
|
agentRootDir: explicitAgentRootDir
|
|
@@ -11826,7 +12027,7 @@ async function runSyncSessions(argv) {
|
|
|
11826
12027
|
runtimeSlotStore: createApiRuntimeSlotStore({ agent: ctx.agent }),
|
|
11827
12028
|
taskReader: ctx.agent.tasks
|
|
11828
12029
|
}, {
|
|
11829
|
-
agentName:
|
|
12030
|
+
agentName: identity.agent,
|
|
11830
12031
|
dryRun: values["dry-run"] === true,
|
|
11831
12032
|
limit,
|
|
11832
12033
|
runtimeProfileId: values["runtime-profile-id"],
|
|
@@ -12003,7 +12204,7 @@ async function writeCache(cache) {
|
|
|
12003
12204
|
}
|
|
12004
12205
|
//#endregion
|
|
12005
12206
|
//#region src/version.ts
|
|
12006
|
-
var DAEMON_VERSION = "0.
|
|
12207
|
+
var DAEMON_VERSION = "0.58.0";
|
|
12007
12208
|
//#endregion
|
|
12008
12209
|
//#region src/cli.ts
|
|
12009
12210
|
async function runAgentDaemonCli(options) {
|