@corenel/sidecar 0.6.2 → 0.7.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/cli.js +183 -19
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -22488,6 +22488,10 @@ var SidecarServer = class {
|
|
|
22488
22488
|
* is the session layer's call, because the operator's permission lives
|
|
22489
22489
|
* there; this class would have to duplicate that rule to decide, and a
|
|
22490
22490
|
* duplicated permission rule is one that eventually disagrees. */
|
|
22491
|
+
// NOT from the wire: `allowMutations` is the daemon's own decision,
|
|
22492
|
+
// derived from an agent's `session:` block. A client that could set it
|
|
22493
|
+
// would grant itself mutating tools inside a sandbox. Deliberately
|
|
22494
|
+
// absent -- see session/guestRunner.ts.
|
|
22491
22495
|
this.opts.sessions.run(m2.sessionId, agent, input, onEvent2, identity, { keepTranscript: m2.keepTranscript === true })
|
|
22492
22496
|
) : (agent, input, onEvent2, identity) => this.opts.runner.run(agent, input, onEvent2, identity);
|
|
22493
22497
|
let runId;
|
|
@@ -28252,6 +28256,9 @@ async function readTranscript(files, crewRoot2, agent, runId) {
|
|
|
28252
28256
|
}
|
|
28253
28257
|
|
|
28254
28258
|
// src/session/manager.ts
|
|
28259
|
+
function isErrorStopReason(stopReason) {
|
|
28260
|
+
return stopReason === "error" || (stopReason ?? "").startsWith("error:");
|
|
28261
|
+
}
|
|
28255
28262
|
var SessionManager = class {
|
|
28256
28263
|
constructor(deps) {
|
|
28257
28264
|
this.deps = deps;
|
|
@@ -28337,11 +28344,11 @@ var SessionManager = class {
|
|
|
28337
28344
|
}
|
|
28338
28345
|
this.deps.log?.(`session ${sessionId}: running agent "${agent}" inside the session`);
|
|
28339
28346
|
const kept2 = this.transcriptOf(onEvent, opts?.keepTranscript === true);
|
|
28340
|
-
return this.recorded(session, agent, identity, () => this.deps.guestRunnerFor(sessionId).run(agent, input, kept2.tee, identity), kept2.buffer);
|
|
28347
|
+
return this.recorded(session, agent, identity, () => this.deps.guestRunnerFor(sessionId, { allowMutations: opts?.allowMutations === true }).run(agent, input, kept2.tee, identity), kept2.buffer, opts?.source);
|
|
28341
28348
|
}
|
|
28342
28349
|
this.deps.log?.(`session ${sessionId}: running agent "${agent}" on the host (isolation: none)`);
|
|
28343
28350
|
const kept = this.transcriptOf(onEvent, opts?.keepTranscript === true);
|
|
28344
|
-
return this.recorded(session, agent, identity, () => this.deps.runner.runFrom ? this.deps.runner.runFrom(session.workdir, agent, input, kept.tee, identity, session.stateDir) : this.deps.runner.run(agent, input, kept.tee, identity), kept.buffer);
|
|
28351
|
+
return this.recorded(session, agent, identity, () => this.deps.runner.runFrom ? this.deps.runner.runFrom(session.workdir, agent, input, kept.tee, identity, session.stateDir) : this.deps.runner.run(agent, input, kept.tee, identity, void 0, opts?.source), kept.buffer, opts?.source);
|
|
28345
28352
|
}
|
|
28346
28353
|
/**
|
|
28347
28354
|
* Run, then write one line about it — whatever happened.
|
|
@@ -28379,7 +28386,7 @@ var SessionManager = class {
|
|
|
28379
28386
|
}
|
|
28380
28387
|
};
|
|
28381
28388
|
}
|
|
28382
|
-
async recorded(session, agent, identity, go, transcript) {
|
|
28389
|
+
async recorded(session, agent, identity, go, transcript, source = "chat") {
|
|
28383
28390
|
const startedAt = Date.now();
|
|
28384
28391
|
const keep = (runId) => {
|
|
28385
28392
|
if (!this.deps.recordTranscript || !transcript || transcript.length === 0) return;
|
|
@@ -28397,7 +28404,7 @@ var SessionManager = class {
|
|
|
28397
28404
|
sessionId: session.id,
|
|
28398
28405
|
isolated: this.isolated,
|
|
28399
28406
|
kind: this.deps.backend.kind,
|
|
28400
|
-
source
|
|
28407
|
+
source,
|
|
28401
28408
|
startedAt,
|
|
28402
28409
|
endedAt: Date.now(),
|
|
28403
28410
|
ok,
|
|
@@ -28409,7 +28416,7 @@ var SessionManager = class {
|
|
|
28409
28416
|
void r2.done.then(
|
|
28410
28417
|
(d2) => {
|
|
28411
28418
|
keep(r2.runId);
|
|
28412
|
-
write3(r2.runId, d2.stopReason
|
|
28419
|
+
write3(r2.runId, !isErrorStopReason(d2.stopReason), d2.stopReason);
|
|
28413
28420
|
},
|
|
28414
28421
|
(e2) => {
|
|
28415
28422
|
keep(r2.runId);
|
|
@@ -28731,6 +28738,7 @@ var LABEL_CREATED = "corenel.created";
|
|
|
28731
28738
|
var LABEL_REPO_URL = "corenel.repo.url";
|
|
28732
28739
|
var LABEL_REPO_REF = "corenel.repo.ref";
|
|
28733
28740
|
var LABEL_REPO_COMMIT = "corenel.repo.commit";
|
|
28741
|
+
var LABEL_CREW_SESSION = "corenel.crew.session";
|
|
28734
28742
|
function idFromSandboxName(name) {
|
|
28735
28743
|
const m2 = /^corenel-(sess_\d+)$/.exec(name);
|
|
28736
28744
|
return m2 ? m2[1] : null;
|
|
@@ -28896,7 +28904,8 @@ function makeMicrovmBackend(deps) {
|
|
|
28896
28904
|
[LABEL_REPO_URL]: spec.source.url,
|
|
28897
28905
|
[LABEL_REPO_COMMIT]: spec.source.commit,
|
|
28898
28906
|
...spec.source.ref ? { [LABEL_REPO_REF]: spec.source.ref } : {}
|
|
28899
|
-
} : {}
|
|
28907
|
+
} : {},
|
|
28908
|
+
...spec.sessionName ? { [LABEL_CREW_SESSION]: spec.sessionName } : {}
|
|
28900
28909
|
}
|
|
28901
28910
|
}), `create session ${id}`);
|
|
28902
28911
|
const handle = {
|
|
@@ -28908,7 +28917,8 @@ function makeMicrovmBackend(deps) {
|
|
|
28908
28917
|
// Kept so a run in this session looks for the agent where the creator's
|
|
28909
28918
|
// workspace actually keeps it.
|
|
28910
28919
|
...spec.stateDir ? { stateDir: spec.stateDir } : {},
|
|
28911
|
-
...spec.source ? { source: spec.source } : {}
|
|
28920
|
+
...spec.source ? { source: spec.source } : {},
|
|
28921
|
+
...spec.sessionName ? { sessionName: spec.sessionName } : {}
|
|
28912
28922
|
};
|
|
28913
28923
|
sessions.set(id, handle);
|
|
28914
28924
|
deps.log?.(
|
|
@@ -29011,9 +29021,28 @@ function makeMicrovmBackend(deps) {
|
|
|
29011
29021
|
*
|
|
29012
29022
|
* CAVEAT, deliberate: the log is the SANDBOX's, not this exec's. Entries
|
|
29013
29023
|
* carry an `id` per invocation, but it is not known before the exec starts,
|
|
29014
|
-
* so concurrent execs in one sandbox
|
|
29015
|
-
*
|
|
29016
|
-
*
|
|
29024
|
+
* so concurrent execs in one sandbox interleave.
|
|
29025
|
+
*
|
|
29026
|
+
* NOTHING PREVENTS THAT, and no earlier claim here should be believed.
|
|
29027
|
+
* This comment once said SessionManager admits one run per session at a
|
|
29028
|
+
* time; a correction then said a scheduled agent's runs "already chain on
|
|
29029
|
+
* that queue". BOTH ARE FALSE. `manager.ts` checks existence and status
|
|
29030
|
+
* only; `SessionStatus` is 'created' | 'paused' | 'destroyed' (types.ts)
|
|
29031
|
+
* with no 'running'; and `RunQueue` (runnerService.ts) wraps the HOST
|
|
29032
|
+
* executor, which the bound path never reaches -- `dispatcher.startInSession`
|
|
29033
|
+
* goes straight to `sessions.run` -> `crewSessions.ts` -> `manager.run`.
|
|
29034
|
+
* Nothing anywhere serializes two runs into one session.
|
|
29035
|
+
*
|
|
29036
|
+
* What NARROWS the window is weaker than a lock and worth stating exactly:
|
|
29037
|
+
* a bound agent gets its own box (spec D3), and the dispatcher dedupes on
|
|
29038
|
+
* an in-flight key of `owner:agent:trigger`.
|
|
29039
|
+
*
|
|
29040
|
+
* What is STILL OPEN, inside the scheduled path: an agent carrying both a
|
|
29041
|
+
* `schedule` and a `continuous` trigger holds two DISTINCT in-flight keys,
|
|
29042
|
+
* so both fire concurrently by design (dispatcher.ts says so in as many
|
|
29043
|
+
* words), and `resolveCrewSession` keys reuse on (name, workdir) only --
|
|
29044
|
+
* so both land in the same sandbox and interleave in one log. The browser
|
|
29045
|
+
* can likewise send two runs into one session.
|
|
29017
29046
|
*/
|
|
29018
29047
|
...deps.stream ? {
|
|
29019
29048
|
execStream(id, command, onLine, opts) {
|
|
@@ -29157,6 +29186,7 @@ function makeMicrovmBackend(deps) {
|
|
|
29157
29186
|
const repoUrl = labels[LABEL_REPO_URL];
|
|
29158
29187
|
const repoCommit = labels[LABEL_REPO_COMMIT];
|
|
29159
29188
|
const repoRef = labels[LABEL_REPO_REF];
|
|
29189
|
+
const sessionName = labels[LABEL_CREW_SESSION];
|
|
29160
29190
|
sessions.set(id, {
|
|
29161
29191
|
id,
|
|
29162
29192
|
status: statusFromSandbox(row2.status),
|
|
@@ -29164,6 +29194,7 @@ function makeMicrovmBackend(deps) {
|
|
|
29164
29194
|
createdAt: Number.isFinite(stamped) && stamped > 0 ? stamped : row2.createdAt ?? now2(),
|
|
29165
29195
|
...owner ? { owner } : {},
|
|
29166
29196
|
...repoUrl && repoCommit ? { source: { url: repoUrl, commit: repoCommit, ...repoRef ? { ref: repoRef } : {} } } : {},
|
|
29197
|
+
...sessionName ? { sessionName } : {},
|
|
29167
29198
|
/* THE ONLY PLACE THIS IS EVER SET TRUE. `create()` never touches it,
|
|
29168
29199
|
* so a card can tell "this daemon booted it" from "this daemon
|
|
29169
29200
|
* found it already running" — procs.ts's own words: a running
|
|
@@ -29263,10 +29294,11 @@ function makeMicrovmBackend(deps) {
|
|
|
29263
29294
|
}
|
|
29264
29295
|
|
|
29265
29296
|
// src/session/guestRunner.ts
|
|
29266
|
-
var DEFAULT_GUEST_COMMAND = (agent, input) => [
|
|
29297
|
+
var DEFAULT_GUEST_COMMAND = (agent, input, opts) => [
|
|
29267
29298
|
"corenel",
|
|
29268
29299
|
"run-agent",
|
|
29269
29300
|
agent,
|
|
29301
|
+
...opts.allowMutations ? ["--allow-mutations"] : [],
|
|
29270
29302
|
"--input",
|
|
29271
29303
|
input
|
|
29272
29304
|
];
|
|
@@ -29311,7 +29343,7 @@ function makeGuestRunner(deps) {
|
|
|
29311
29343
|
const exec = deps.backend.exec.bind(deps.backend);
|
|
29312
29344
|
const runId = `grun_${++seq3}`;
|
|
29313
29345
|
deps.log?.(`session ${sessionId}: running "${agent}" inside the guest (${runId})`);
|
|
29314
|
-
const command = build(agent, input);
|
|
29346
|
+
const command = build(agent, input, { allowMutations: deps.allowMutations === true });
|
|
29315
29347
|
const timeout = deps.timeoutMs === void 0 ? void 0 : { timeoutMs: deps.timeoutMs };
|
|
29316
29348
|
const seen = [];
|
|
29317
29349
|
let skipped = 0;
|
|
@@ -29519,9 +29551,10 @@ async function wireSessions(opts) {
|
|
|
29519
29551
|
...opts.recordTranscript ? { recordTranscript: opts.recordTranscript } : {},
|
|
29520
29552
|
// One runner per session, built on demand: the manager knows which session a
|
|
29521
29553
|
// run belongs to, so nothing has to infer it.
|
|
29522
|
-
guestRunnerFor: (sessionId) => makeGuestRunner({
|
|
29554
|
+
guestRunnerFor: (sessionId, guestOpts) => makeGuestRunner({
|
|
29523
29555
|
backend,
|
|
29524
29556
|
sessionId: () => sessionId,
|
|
29557
|
+
allowMutations: guestOpts.allowMutations,
|
|
29525
29558
|
...opts.log ? { log: opts.log } : {}
|
|
29526
29559
|
}),
|
|
29527
29560
|
...opts.log ? { log: opts.log } : {}
|
|
@@ -29533,6 +29566,26 @@ async function wireSessions(opts) {
|
|
|
29533
29566
|
return { manager, backend, port: makeSessionsPort({ manager, backend, guestEnv, ...portExtras(opts) }) };
|
|
29534
29567
|
}
|
|
29535
29568
|
|
|
29569
|
+
// src/runner/crewSessions.ts
|
|
29570
|
+
function makeCrewSessions(src2, opts) {
|
|
29571
|
+
if (!src2?.manager.isolated) return void 0;
|
|
29572
|
+
const { manager, backend } = src2;
|
|
29573
|
+
return {
|
|
29574
|
+
list: () => backend.list(),
|
|
29575
|
+
adopt: () => backend.adopt?.() ?? Promise.resolve(void 0),
|
|
29576
|
+
create: async (spec) => {
|
|
29577
|
+
const env = await opts.guestEnv().catch(() => ({}));
|
|
29578
|
+
return manager.create({
|
|
29579
|
+
workdir: spec.workdir,
|
|
29580
|
+
sessionName: spec.sessionName,
|
|
29581
|
+
stateDir: opts.stateDir,
|
|
29582
|
+
env: { ...env, CORENEL_STATE_DIR: opts.stateDir }
|
|
29583
|
+
});
|
|
29584
|
+
},
|
|
29585
|
+
run: (sessionId, agent, input, onEvent, identity, runOpts) => manager.run(sessionId, agent, input, onEvent, identity, runOpts)
|
|
29586
|
+
};
|
|
29587
|
+
}
|
|
29588
|
+
|
|
29536
29589
|
// src/runner/owners.ts
|
|
29537
29590
|
import { readdir } from "node:fs/promises";
|
|
29538
29591
|
import { join as join8 } from "node:path";
|
|
@@ -30220,6 +30273,41 @@ var CREW_EXCLUDED_TOOLS = /* @__PURE__ */ new Set(["ask_user", "spawn_agent", "s
|
|
|
30220
30273
|
|
|
30221
30274
|
// ../crew/store.ts
|
|
30222
30275
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
30276
|
+
|
|
30277
|
+
// ../crew/sessionBinding.ts
|
|
30278
|
+
var SESSION_NAME_RE = /^[a-z0-9_-]{1,32}$/;
|
|
30279
|
+
function isValidSessionName(name) {
|
|
30280
|
+
return SESSION_NAME_RE.test(name);
|
|
30281
|
+
}
|
|
30282
|
+
function parseSessionBinding(meta) {
|
|
30283
|
+
const raw = meta.session;
|
|
30284
|
+
if (raw === void 0) return { kind: "absent" };
|
|
30285
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
30286
|
+
return { kind: "malformed", reason: "session: must be a block with `name:` and `workdir:`" };
|
|
30287
|
+
}
|
|
30288
|
+
const o2 = raw;
|
|
30289
|
+
const name = typeof o2.name === "string" ? o2.name : "";
|
|
30290
|
+
const workdir = typeof o2.workdir === "string" ? o2.workdir.trim() : "";
|
|
30291
|
+
if (!isValidSessionName(name)) {
|
|
30292
|
+
return {
|
|
30293
|
+
kind: "malformed",
|
|
30294
|
+
reason: `session.name ${JSON.stringify(name)} must be 1-32 characters of a-z, 0-9, "-" or "_"`
|
|
30295
|
+
};
|
|
30296
|
+
}
|
|
30297
|
+
if (!workdir) return { kind: "malformed", reason: "session.workdir is required" };
|
|
30298
|
+
return {
|
|
30299
|
+
kind: "binding",
|
|
30300
|
+
binding: {
|
|
30301
|
+
name,
|
|
30302
|
+
workdir,
|
|
30303
|
+
// Anything but an explicit `false` is true, matching how `enabled` is read
|
|
30304
|
+
// in store.ts — absent means the default, and the default is on.
|
|
30305
|
+
createIfMissing: o2.createIfMissing !== false
|
|
30306
|
+
}
|
|
30307
|
+
};
|
|
30308
|
+
}
|
|
30309
|
+
|
|
30310
|
+
// ../crew/store.ts
|
|
30223
30311
|
var TRIGGER_TYPES = [
|
|
30224
30312
|
"manual",
|
|
30225
30313
|
"file-change",
|
|
@@ -30305,6 +30393,7 @@ async function loadCrewAgentAt(files, root, name, scope = "workspace") {
|
|
|
30305
30393
|
if (!isValidAgentName(name)) throw new Error(`invalid crew agent name: ${name}`);
|
|
30306
30394
|
const dir = `${root}/${name}`;
|
|
30307
30395
|
const { meta, body: body2 } = parseFrontmatter(await readOr(files, `${dir}/AGENT.md`));
|
|
30396
|
+
const session = parseSessionBinding(meta);
|
|
30308
30397
|
return {
|
|
30309
30398
|
name,
|
|
30310
30399
|
jobTitle: typeof meta.jobTitle === "string" ? meta.jobTitle : "",
|
|
@@ -30319,7 +30408,9 @@ async function loadCrewAgentAt(files, root, name, scope = "workspace") {
|
|
|
30319
30408
|
policy: parsePolicyYaml(await readOr(files, `${dir}/POLICY.yaml`), name),
|
|
30320
30409
|
settings: parseSettings(await readOr(files, `${dir}/settings.json`, "{}")),
|
|
30321
30410
|
budget: parseBudget(meta),
|
|
30322
|
-
canCall: parseCanCall(meta)
|
|
30411
|
+
canCall: parseCanCall(meta),
|
|
30412
|
+
...session.kind === "binding" ? { session: session.binding } : {},
|
|
30413
|
+
...session.kind === "malformed" ? { sessionError: session.reason } : {}
|
|
30323
30414
|
};
|
|
30324
30415
|
}
|
|
30325
30416
|
async function loadCrewAgentAtIfExists(files, root, name, scope = "workspace") {
|
|
@@ -42618,6 +42709,26 @@ function makeTriggerSchedule(opts) {
|
|
|
42618
42709
|
return { due, nextWakeAfter, registrations };
|
|
42619
42710
|
}
|
|
42620
42711
|
|
|
42712
|
+
// src/session/crewSessionResolve.ts
|
|
42713
|
+
function sameDir(a2, b2) {
|
|
42714
|
+
const norm2 = (p2) => p2.split("\\").join("/").replace(/[/]+$/, "").toLowerCase();
|
|
42715
|
+
return norm2(a2) === norm2(b2);
|
|
42716
|
+
}
|
|
42717
|
+
function resolveCrewSession(binding, handles) {
|
|
42718
|
+
const matches = handles.filter((h2) => h2.status === "created" && h2.sessionName === binding.name && sameDir(h2.workdir, binding.workdir));
|
|
42719
|
+
if (matches.length) {
|
|
42720
|
+
const newest = matches.reduce((a2, b2) => b2.createdAt > a2.createdAt ? b2 : a2);
|
|
42721
|
+
return { kind: "reuse", sessionId: newest.id };
|
|
42722
|
+
}
|
|
42723
|
+
if (!binding.createIfMissing) {
|
|
42724
|
+
return {
|
|
42725
|
+
kind: "refuse",
|
|
42726
|
+
reason: `no live session "${binding.name}" on ${binding.workdir}, and createIfMissing is off`
|
|
42727
|
+
};
|
|
42728
|
+
}
|
|
42729
|
+
return { kind: "create", spec: { workdir: binding.workdir, sessionName: binding.name } };
|
|
42730
|
+
}
|
|
42731
|
+
|
|
42621
42732
|
// src/runner/dispatcher.ts
|
|
42622
42733
|
function hasBudget2(def) {
|
|
42623
42734
|
return def.budget?.maxUsd != null || def.budget?.maxTokens != null;
|
|
@@ -42641,11 +42752,17 @@ function makeDispatcher(opts) {
|
|
|
42641
42752
|
});
|
|
42642
42753
|
return next;
|
|
42643
42754
|
}
|
|
42644
|
-
async function recordFailedDispatch(d2, root, startedAt, runId, message) {
|
|
42755
|
+
async function recordFailedDispatch(d2, root, startedAt, runId, message, where) {
|
|
42645
42756
|
await appendRun(root.files, root.root, {
|
|
42646
42757
|
runId,
|
|
42647
42758
|
agent: d2.agent,
|
|
42648
|
-
where:
|
|
42759
|
+
/* PASSED, NOT ASSUMED. This said `where: 'host'` unconditionally, so
|
|
42760
|
+
* every D5 refusal -- no session backend, `createIfMissing: false`, a
|
|
42761
|
+
* `create` that threw, a malformed `session:` block -- told the operator
|
|
42762
|
+
* the run had happened on the host, which is the one thing this feature
|
|
42763
|
+
* guarantees did NOT happen. A bound agent that refuses files a
|
|
42764
|
+
* `where: 'session'` row: nothing ran, and nothing ran THERE. */
|
|
42765
|
+
where,
|
|
42649
42766
|
source: d2.trigger,
|
|
42650
42767
|
startedAt,
|
|
42651
42768
|
endedAt: now2(),
|
|
@@ -42653,6 +42770,35 @@ function makeDispatcher(opts) {
|
|
|
42653
42770
|
stopReason: message
|
|
42654
42771
|
}, log);
|
|
42655
42772
|
}
|
|
42773
|
+
async function startInSession(binding, d2) {
|
|
42774
|
+
const sessions = opts.sessions?.();
|
|
42775
|
+
if (!sessions) {
|
|
42776
|
+
throw new Error(
|
|
42777
|
+
`crew agent "${d2.agent}" is bound to session "${binding.name}", but this daemon has no session backend`
|
|
42778
|
+
);
|
|
42779
|
+
}
|
|
42780
|
+
try {
|
|
42781
|
+
await sessions.adopt?.();
|
|
42782
|
+
} catch {
|
|
42783
|
+
}
|
|
42784
|
+
const decision = resolveCrewSession(binding, sessions.list());
|
|
42785
|
+
if (decision.kind === "refuse") {
|
|
42786
|
+
throw new Error(`crew agent "${d2.agent}": ${decision.reason}`);
|
|
42787
|
+
}
|
|
42788
|
+
const sessionId = decision.kind === "reuse" ? decision.sessionId : (await sessions.create(decision.spec)).id;
|
|
42789
|
+
return sessions.run(
|
|
42790
|
+
sessionId,
|
|
42791
|
+
d2.agent,
|
|
42792
|
+
d2.input,
|
|
42793
|
+
() => {
|
|
42794
|
+
},
|
|
42795
|
+
{ owner: d2.owner },
|
|
42796
|
+
// `source` is the trigger, not 'chat': the session's own run record is
|
|
42797
|
+
// the only durable answer to "why did this happen", and it is written by
|
|
42798
|
+
// the session layer, not by this file's failure path.
|
|
42799
|
+
{ allowMutations: true, source: d2.trigger }
|
|
42800
|
+
);
|
|
42801
|
+
}
|
|
42656
42802
|
async function fire(d2) {
|
|
42657
42803
|
const dispatchedAt = now2();
|
|
42658
42804
|
const key = `${d2.owner}:${d2.agent}:${d2.trigger}`;
|
|
@@ -42661,6 +42807,7 @@ function makeDispatcher(opts) {
|
|
|
42661
42807
|
return;
|
|
42662
42808
|
}
|
|
42663
42809
|
inFlight.add(key);
|
|
42810
|
+
let where = "host";
|
|
42664
42811
|
try {
|
|
42665
42812
|
const roots = await opts.ownerRoots();
|
|
42666
42813
|
const match = roots.find((r2) => r2.owner === d2.owner);
|
|
@@ -42673,12 +42820,17 @@ function makeDispatcher(opts) {
|
|
|
42673
42820
|
log(`dispatcher: agent "${d2.agent}" not found (owner "${d2.owner}") -- skipped`);
|
|
42674
42821
|
return;
|
|
42675
42822
|
}
|
|
42823
|
+
where = def.session || def.sessionError ? "session" : "host";
|
|
42676
42824
|
if (d2.trigger === "continuous" && !hasBudget2(def)) {
|
|
42677
42825
|
log(`refusing continuous run for "${d2.agent}" (owner "${d2.owner}"): a budget (maxUsd or maxTokens) is required`);
|
|
42678
42826
|
return;
|
|
42679
42827
|
}
|
|
42680
42828
|
await withAgentStateLock(`${d2.owner}:${d2.agent}`, () => persistDispatch(d2, match));
|
|
42681
|
-
|
|
42829
|
+
if (def.sessionError) {
|
|
42830
|
+
throw new Error(`crew agent "${d2.agent}" has an unusable session: block -- ${def.sessionError}`);
|
|
42831
|
+
}
|
|
42832
|
+
const binding = def.session;
|
|
42833
|
+
const started = binding ? await startInSession(binding, d2) : await runner.run(d2.agent, d2.input, () => {
|
|
42682
42834
|
}, { owner: d2.owner }, d2.generation ?? 0, d2.trigger);
|
|
42683
42835
|
const startedAt = now2();
|
|
42684
42836
|
const announce = (extra) => {
|
|
@@ -42710,7 +42862,7 @@ function makeDispatcher(opts) {
|
|
|
42710
42862
|
announce({ endedAt: now2(), ok: false, stopReason: msg });
|
|
42711
42863
|
log(`dispatcher: run of "${d2.agent}" (${d2.trigger}) failed -- ${msg}`);
|
|
42712
42864
|
void settleSelfDirected(d2, match);
|
|
42713
|
-
void recordFailedDispatch(d2, match, startedAt, started.runId, msg);
|
|
42865
|
+
void recordFailedDispatch(d2, match, startedAt, started.runId, msg, where);
|
|
42714
42866
|
}
|
|
42715
42867
|
);
|
|
42716
42868
|
} catch (err) {
|
|
@@ -42719,7 +42871,7 @@ function makeDispatcher(opts) {
|
|
|
42719
42871
|
try {
|
|
42720
42872
|
const roots = await opts.ownerRoots();
|
|
42721
42873
|
const root = roots.find((r2) => r2.owner === d2.owner);
|
|
42722
|
-
if (root) await recordFailedDispatch(d2, root, dispatchedAt, `dispatch-${dispatchedAt}`, msg);
|
|
42874
|
+
if (root) await recordFailedDispatch(d2, root, dispatchedAt, `dispatch-${dispatchedAt}`, msg, where);
|
|
42723
42875
|
} catch {
|
|
42724
42876
|
}
|
|
42725
42877
|
} finally {
|
|
@@ -43409,6 +43561,9 @@ function startDaemonCore(opts) {
|
|
|
43409
43561
|
// it -- the same contract `schedule_self` has. `clock` is declared below
|
|
43410
43562
|
// and only read when this fires, long after it is initialized.
|
|
43411
43563
|
onScheduled: () => clock.wake(),
|
|
43564
|
+
// Read per fire(), so a daemon whose session layer comes up after this
|
|
43565
|
+
// core still routes bound agents into it.
|
|
43566
|
+
...opts.sessions ? { sessions: opts.sessions } : {},
|
|
43412
43567
|
log
|
|
43413
43568
|
});
|
|
43414
43569
|
const roomProgressSubs = /* @__PURE__ */ new Set();
|
|
@@ -45520,6 +45675,10 @@ function layerConfig(args, explicit, file) {
|
|
|
45520
45675
|
from[key] = "file";
|
|
45521
45676
|
if (key === "endpoint") out.endpointExplicit = true;
|
|
45522
45677
|
}
|
|
45678
|
+
if (out.allowLan) {
|
|
45679
|
+
if (from.host === "default") out.host = "0.0.0.0";
|
|
45680
|
+
if (from.selfSigned === "default" && !out.tlsCert && !out.tlsKey) out.selfSigned = true;
|
|
45681
|
+
}
|
|
45523
45682
|
return { args: out, from, warnings };
|
|
45524
45683
|
}
|
|
45525
45684
|
|
|
@@ -47653,6 +47812,7 @@ async function main() {
|
|
|
47653
47812
|
const attendance = makeAttendanceRegistry({ log: slog });
|
|
47654
47813
|
const runs = makeRunHub({ log: slog });
|
|
47655
47814
|
let core;
|
|
47815
|
+
let crewSessions;
|
|
47656
47816
|
const pauses = args.daemon ? await loadPauseStore(args.stateDir) : void 0;
|
|
47657
47817
|
let controlPlane = null;
|
|
47658
47818
|
const runner = args.daemon ? new NodeRunnerService({
|
|
@@ -47767,6 +47927,9 @@ async function main() {
|
|
|
47767
47927
|
// through the attendance registry -- there is no room-progress member on
|
|
47768
47928
|
// AttendanceRegistry (unlike runActivity/registrations), and adding one
|
|
47769
47929
|
// is out of this task's scope.
|
|
47930
|
+
// Bound crew agents run in the session layer built below -- read through
|
|
47931
|
+
// the holder, never captured by value, because it does not exist yet.
|
|
47932
|
+
sessions: () => crewSessions,
|
|
47770
47933
|
onRoomProgress: (room) => slog(`room ${room.roomId}: ${room.turns} turn(s), ${room.participants.length} participant(s)${room.closed ? ` -- closed (${room.closed.reason})` : ""}`)
|
|
47771
47934
|
});
|
|
47772
47935
|
}
|
|
@@ -47892,6 +48055,7 @@ async function main() {
|
|
|
47892
48055
|
git: createGitSource({ areas: args.cloneUnder, run: makeGitRunner(), log: slog }),
|
|
47893
48056
|
approveCheckout: (dir) => rootsController.addFromClone(dir)
|
|
47894
48057
|
}) : void 0;
|
|
48058
|
+
crewSessions = makeCrewSessions(sessions, { stateDir: stateDirName(), guestEnv });
|
|
47895
48059
|
const handle = await startSidecar({
|
|
47896
48060
|
port: args.port,
|
|
47897
48061
|
host: args.host,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corenel/sidecar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Elastic-2.0",
|
|
6
6
|
"description": "Prompd sidecar — a local capability server. Run it next to Prompd (prompd.app) so the browser app can reach your machine: local LLMs (Ollama/LM Studio/vLLM) without CORS, your filesystem, and (opt-in) shell.",
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"esbuild": "^0.21.5",
|
|
37
37
|
"tsx": "^4.19.2",
|
|
38
38
|
"typescript": "^5.9.3",
|
|
39
|
-
"@corenel/protocol": "0.6.1",
|
|
40
|
-
"@corenel/harness": "0.6.1",
|
|
41
39
|
"@corenel/tools-node": "0.3.1",
|
|
42
|
-
"@corenel/
|
|
40
|
+
"@corenel/protocol": "0.6.1",
|
|
41
|
+
"@corenel/crew": "0.0.0",
|
|
42
|
+
"@corenel/harness": "0.6.1"
|
|
43
43
|
},
|
|
44
44
|
"repository": {
|
|
45
45
|
"type": "git",
|