@themoltnet/agent-daemon 0.18.0 → 0.19.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/main.js +564 -332
- package/package.json +6 -6
package/dist/main.js
CHANGED
|
@@ -8,12 +8,13 @@ import { PgInstrumentation } from "@opentelemetry/instrumentation-pg";
|
|
|
8
8
|
import { PinoInstrumentation } from "@opentelemetry/instrumentation-pino";
|
|
9
9
|
import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
|
|
10
10
|
import crypto$1, { createHash } from "crypto";
|
|
11
|
-
import { createHash as createHash$1 } from "node:crypto";
|
|
11
|
+
import crypto$2, { createHash as createHash$1 } from "node:crypto";
|
|
12
12
|
import path, { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
13
13
|
import { parseArgs, parseEnv, promisify } from "node:util";
|
|
14
14
|
import { execFile, execFileSync } from "node:child_process";
|
|
15
|
-
import { accessSync, constants, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
15
|
+
import fs, { accessSync, constants, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
16
16
|
import { DatabaseSync } from "node:sqlite";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
17
18
|
import { ROOT_CONTEXT, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
|
|
18
19
|
import { pino, transport } from "pino";
|
|
19
20
|
import { readFile } from "node:fs/promises";
|
|
@@ -4375,6 +4376,54 @@ function validateRubricWeights(rubric) {
|
|
|
4375
4376
|
return null;
|
|
4376
4377
|
}
|
|
4377
4378
|
//#endregion
|
|
4379
|
+
//#region ../../libs/tasks/src/runtime-models.ts
|
|
4380
|
+
/**
|
|
4381
|
+
* Runtime model catalog: a list of supported provider/model couples that
|
|
4382
|
+
* MoltNet daemons can target. Backed by the `runtime_models` table.
|
|
4383
|
+
*
|
|
4384
|
+
* Scope is intrinsic to the row:
|
|
4385
|
+
* - `teamId == null` => global entry (MoltNet-seeded, read-only to most callers)
|
|
4386
|
+
* - `teamId != null` => team-owned custom entry
|
|
4387
|
+
*
|
|
4388
|
+
* The REST API exposes a single shape regardless of scope; the team header
|
|
4389
|
+
* gates which rows are returned.
|
|
4390
|
+
*/
|
|
4391
|
+
var RuntimeModelProvider = String$1({
|
|
4392
|
+
minLength: 1,
|
|
4393
|
+
maxLength: 100,
|
|
4394
|
+
pattern: "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,99}$"
|
|
4395
|
+
});
|
|
4396
|
+
var RuntimeModelName = String$1({
|
|
4397
|
+
minLength: 1,
|
|
4398
|
+
maxLength: 200,
|
|
4399
|
+
pattern: "^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,199}$"
|
|
4400
|
+
});
|
|
4401
|
+
var RuntimeModelCapabilities = Record(String$1({
|
|
4402
|
+
minLength: 1,
|
|
4403
|
+
maxLength: 64
|
|
4404
|
+
}), Union([
|
|
4405
|
+
Boolean$1(),
|
|
4406
|
+
Number$1(),
|
|
4407
|
+
String$1({ maxLength: 256 })
|
|
4408
|
+
]));
|
|
4409
|
+
_Object_({
|
|
4410
|
+
id: String$1({ format: "uuid" }),
|
|
4411
|
+
teamId: Union([String$1({ format: "uuid" }), Null()]),
|
|
4412
|
+
provider: RuntimeModelProvider,
|
|
4413
|
+
model: RuntimeModelName,
|
|
4414
|
+
displayName: Union([String$1({ maxLength: 200 }), Null()]),
|
|
4415
|
+
description: Union([String$1({ maxLength: 4096 }), Null()]),
|
|
4416
|
+
capabilities: RuntimeModelCapabilities,
|
|
4417
|
+
isActive: Boolean$1(),
|
|
4418
|
+
createdByAgentId: Union([String$1({ format: "uuid" }), Null()]),
|
|
4419
|
+
createdByHumanId: Union([String$1({ format: "uuid" }), Null()]),
|
|
4420
|
+
createdAt: String$1({ format: "date-time" }),
|
|
4421
|
+
updatedAt: String$1({ format: "date-time" })
|
|
4422
|
+
}, {
|
|
4423
|
+
$id: "RuntimeModel",
|
|
4424
|
+
additionalProperties: false
|
|
4425
|
+
});
|
|
4426
|
+
//#endregion
|
|
4378
4427
|
//#region ../../libs/tasks/src/runtime-profiles.ts
|
|
4379
4428
|
var RuntimeProfileName = String$1({
|
|
4380
4429
|
minLength: 1,
|
|
@@ -5756,20 +5805,17 @@ var FreeformOutput = _Object_({
|
|
|
5756
5805
|
* 3. `freeform.sourceAttemptNotCompleted` — named attempt is missing
|
|
5757
5806
|
* or not in `completed` state; warm continuation only makes sense
|
|
5758
5807
|
* once the parent has produced a terminal output.
|
|
5759
|
-
* 4. `freeform.
|
|
5760
|
-
* surface for copy-on-write continuation tracked in #1293; v1
|
|
5761
|
-
* rejects it server-side so daemons never have to branch.
|
|
5762
|
-
* 5. `freeform.executionWorkspaceNotInheritable` — caller set
|
|
5808
|
+
* 4. `freeform.executionWorkspaceNotInheritable` — caller set
|
|
5763
5809
|
* `execution.workspace` together with `continueFrom`. Workspace
|
|
5764
5810
|
* mode for a continuation is inherited from the parent slot
|
|
5765
5811
|
* (`maybeAttachWarmSlotContext` forces `dedicated_worktree` +
|
|
5766
5812
|
* the parent's worktreeBranch), so any caller-supplied override
|
|
5767
5813
|
* is silently dropped at the daemon plan stage. Reject explicitly
|
|
5768
5814
|
* so misconfiguration surfaces at create time.
|
|
5769
|
-
*
|
|
5815
|
+
* 5. `freeform.sourceNotResumeEligible` — `daemonState` is null or
|
|
5770
5816
|
* `slotResumableUntil` is null. Older completions (pre-#1287) and
|
|
5771
5817
|
* daemons that opt out fall here.
|
|
5772
|
-
*
|
|
5818
|
+
* 6. `freeform.sourceResumeExpired` — `slotResumableUntil` is in the
|
|
5773
5819
|
* past; the warm slot's TTL has elapsed and no daemon is
|
|
5774
5820
|
* guaranteed to still hold it.
|
|
5775
5821
|
*
|
|
@@ -5790,11 +5836,6 @@ async function validateFreeformInputAsync(input, ctx) {
|
|
|
5790
5836
|
message: `Source task type '${source.taskType}' is not continuable; only freeform → freeform is supported in v1`,
|
|
5791
5837
|
code: "freeform.sourceTaskTypeNotSupported"
|
|
5792
5838
|
}];
|
|
5793
|
-
if (cf.mode === "fork") return [{
|
|
5794
|
-
field: "input/continueFrom/mode",
|
|
5795
|
-
message: "fork mode not yet implemented; see https://github.com/getlarge/themoltnet/issues/1293",
|
|
5796
|
-
code: "freeform.forkModeNotImplemented"
|
|
5797
|
-
}];
|
|
5798
5839
|
if (input.execution?.workspace) return [{
|
|
5799
5840
|
field: "input/execution/workspace",
|
|
5800
5841
|
message: "execution.workspace is inherited from the parent slot when continueFrom is set; omit it",
|
|
@@ -8041,7 +8082,7 @@ function Errors$1(...args) {
|
|
|
8041
8082
|
//#endregion
|
|
8042
8083
|
//#region ../../node_modules/.pnpm/typebox@1.2.8/node_modules/typebox/build/schema/check.mjs
|
|
8043
8084
|
/** Checks a value against the provided schema */
|
|
8044
|
-
function Check$
|
|
8085
|
+
function Check$3(...args) {
|
|
8045
8086
|
const [context, schema, value] = Match$3(args, {
|
|
8046
8087
|
3: (context, schema, value) => [
|
|
8047
8088
|
context,
|
|
@@ -8059,7 +8100,7 @@ function Check$1(...args) {
|
|
|
8059
8100
|
//#endregion
|
|
8060
8101
|
//#region ../../node_modules/.pnpm/typebox@1.2.8/node_modules/typebox/build/value/check/check.mjs
|
|
8061
8102
|
/** Checks a value matches the provided type. */
|
|
8062
|
-
function Check(...args) {
|
|
8103
|
+
function Check$2(...args) {
|
|
8063
8104
|
const [context, type, value] = Match$3(args, {
|
|
8064
8105
|
3: (context, type, value) => [
|
|
8065
8106
|
context,
|
|
@@ -8072,7 +8113,7 @@ function Check(...args) {
|
|
|
8072
8113
|
value
|
|
8073
8114
|
]
|
|
8074
8115
|
});
|
|
8075
|
-
return Check$
|
|
8116
|
+
return Check$3(context, type, value);
|
|
8076
8117
|
}
|
|
8077
8118
|
//#endregion
|
|
8078
8119
|
//#region ../../node_modules/.pnpm/typebox@1.2.8/node_modules/typebox/build/value/errors/errors.mjs
|
|
@@ -8159,7 +8200,7 @@ function FromObject$4(context, type, value) {
|
|
|
8159
8200
|
value[key] = FromType$3(context, type.properties[key], value[key]);
|
|
8160
8201
|
continue;
|
|
8161
8202
|
}
|
|
8162
|
-
if (IsBoolean$2(additionalProperties) && IsEqual(additionalProperties, true) || IsSchema$1(additionalProperties) && Check(context, additionalProperties, value[key])) {
|
|
8203
|
+
if (IsBoolean$2(additionalProperties) && IsEqual(additionalProperties, true) || IsSchema$1(additionalProperties) && Check$2(context, additionalProperties, value[key])) {
|
|
8163
8204
|
value[key] = FromType$3(context, additionalProperties, value[key]);
|
|
8164
8205
|
continue;
|
|
8165
8206
|
}
|
|
@@ -8178,7 +8219,7 @@ function FromRecord$3(context, type, value) {
|
|
|
8178
8219
|
value[key] = FromType$3(context, recordValue, value[key]);
|
|
8179
8220
|
continue;
|
|
8180
8221
|
}
|
|
8181
|
-
if (IsBoolean$2(additionalProperties) && IsEqual(additionalProperties, true) || IsSchema$1(additionalProperties) && Check(context, additionalProperties, value[key])) {
|
|
8222
|
+
if (IsBoolean$2(additionalProperties) && IsEqual(additionalProperties, true) || IsSchema$1(additionalProperties) && Check$2(context, additionalProperties, value[key])) {
|
|
8182
8223
|
value[key] = FromType$3(context, additionalProperties, value[key]);
|
|
8183
8224
|
continue;
|
|
8184
8225
|
}
|
|
@@ -8261,7 +8302,7 @@ function UnionPrioritySort(types, order = 1) {
|
|
|
8261
8302
|
function FromUnion$3(context, type, value) {
|
|
8262
8303
|
for (const schema of UnionPrioritySort(type.anyOf)) {
|
|
8263
8304
|
const clean = FromType$3(context, schema, Clone(value));
|
|
8264
|
-
if (Check(context, schema, clean)) return clean;
|
|
8305
|
+
if (Check$2(context, schema, clean)) return clean;
|
|
8265
8306
|
}
|
|
8266
8307
|
return value;
|
|
8267
8308
|
}
|
|
@@ -8562,8 +8603,8 @@ function FromUndefined(_context, _type, value) {
|
|
|
8562
8603
|
//#endregion
|
|
8563
8604
|
//#region ../../node_modules/.pnpm/typebox@1.2.8/node_modules/typebox/build/value/convert/from_union.mjs
|
|
8564
8605
|
function FromUnion$2(context, type, value) {
|
|
8565
|
-
if (type.anyOf.some((type) => Check(context, type, value))) return value;
|
|
8566
|
-
const selected = type.anyOf.map((type) => FromType$2(context, type, Clone(value))).find((value) => Check(context, type, value));
|
|
8606
|
+
if (type.anyOf.some((type) => Check$2(context, type, value))) return value;
|
|
8607
|
+
const selected = type.anyOf.map((type) => FromType$2(context, type, Clone(value))).find((value) => Check$2(context, type, value));
|
|
8567
8608
|
return IsUndefined$1(selected) ? value : selected;
|
|
8568
8609
|
}
|
|
8569
8610
|
//#endregion
|
|
@@ -8680,7 +8721,7 @@ function FromTuple$1(context, schema, value) {
|
|
|
8680
8721
|
function FromUnion$1(context, schema, value) {
|
|
8681
8722
|
for (const inner of schema.anyOf) {
|
|
8682
8723
|
const result = FromType$1(context, inner, Clone(value));
|
|
8683
|
-
if (Check(context, inner, result)) return result;
|
|
8724
|
+
if (Check$2(context, inner, result)) return result;
|
|
8684
8725
|
}
|
|
8685
8726
|
return value;
|
|
8686
8727
|
}
|
|
@@ -8870,7 +8911,7 @@ function FromTuple(direction, context, type, value) {
|
|
|
8870
8911
|
//#region ../../node_modules/.pnpm/typebox@1.2.8/node_modules/typebox/build/value/codec/from_union.mjs
|
|
8871
8912
|
function Decode(direction, context, type, value) {
|
|
8872
8913
|
for (const schema of UnionPrioritySort(type.anyOf, 1)) {
|
|
8873
|
-
if (!Check(context, schema, value)) continue;
|
|
8914
|
+
if (!Check$2(context, schema, value)) continue;
|
|
8874
8915
|
return Callback(direction, context, type, FromType(direction, context, schema, value));
|
|
8875
8916
|
}
|
|
8876
8917
|
return value;
|
|
@@ -8879,7 +8920,7 @@ function Encode(direction, context, type, value) {
|
|
|
8879
8920
|
const exterior = Callback(direction, context, type, value);
|
|
8880
8921
|
for (const schema of UnionPrioritySort(type.anyOf, -1)) {
|
|
8881
8922
|
const variant = FromType(direction, context, schema, Clone(exterior));
|
|
8882
|
-
if (!Check(context, schema, variant)) continue;
|
|
8923
|
+
if (!Check$2(context, schema, variant)) continue;
|
|
8883
8924
|
return variant;
|
|
8884
8925
|
}
|
|
8885
8926
|
return exterior;
|
|
@@ -8900,7 +8941,7 @@ var DecodeError = class extends AssertError {
|
|
|
8900
8941
|
}
|
|
8901
8942
|
};
|
|
8902
8943
|
function Assert$2(context, type, value) {
|
|
8903
|
-
if (!Check(context, type, value)) throw new DecodeError(value, Errors(context, type, value));
|
|
8944
|
+
if (!Check$2(context, type, value)) throw new DecodeError(value, Errors(context, type, value));
|
|
8904
8945
|
return value;
|
|
8905
8946
|
}
|
|
8906
8947
|
/** Executes Decode callbacks only */
|
|
@@ -8923,7 +8964,7 @@ var EncodeError = class extends AssertError {
|
|
|
8923
8964
|
}
|
|
8924
8965
|
};
|
|
8925
8966
|
function Assert$1(context, type, value) {
|
|
8926
|
-
if (!Check(context, type, value)) throw new EncodeError(value, Errors(context, type, value));
|
|
8967
|
+
if (!Check$2(context, type, value)) throw new EncodeError(value, Errors(context, type, value));
|
|
8927
8968
|
return value;
|
|
8928
8969
|
}
|
|
8929
8970
|
/** Executes Encode callbacks only */
|
|
@@ -8946,7 +8987,7 @@ var ParseError = class extends AssertError {
|
|
|
8946
8987
|
}
|
|
8947
8988
|
};
|
|
8948
8989
|
function Assert(context, type, value) {
|
|
8949
|
-
if (!Check(context, type, value)) throw new ParseError(value, Errors(context, type, value));
|
|
8990
|
+
if (!Check$2(context, type, value)) throw new ParseError(value, Errors(context, type, value));
|
|
8950
8991
|
return value;
|
|
8951
8992
|
}
|
|
8952
8993
|
Pipeline([
|
|
@@ -9763,6 +9804,17 @@ var ForeignKey$1 = class {
|
|
|
9763
9804
|
return name ?? `${chunks.join("_")}_fk`;
|
|
9764
9805
|
}
|
|
9765
9806
|
};
|
|
9807
|
+
function foreignKey$1(config) {
|
|
9808
|
+
function mappedConfig() {
|
|
9809
|
+
const { name, columns, foreignColumns } = config;
|
|
9810
|
+
return {
|
|
9811
|
+
name,
|
|
9812
|
+
columns,
|
|
9813
|
+
foreignColumns
|
|
9814
|
+
};
|
|
9815
|
+
}
|
|
9816
|
+
return new ForeignKeyBuilder$1(mappedConfig);
|
|
9817
|
+
}
|
|
9766
9818
|
//#endregion
|
|
9767
9819
|
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@opentelemetry+api@1.9.0_@types+better-sqlite3@7.6.13_@types+pg@8.16_374a03c8dde600dcef5a1d8002afafad/node_modules/drizzle-orm/tracing-utils.js
|
|
9768
9820
|
function iife(fn, ...args) {
|
|
@@ -15270,12 +15322,12 @@ var require_helper = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
15270
15322
|
//#region ../../node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js
|
|
15271
15323
|
var require_lib$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
15272
15324
|
__require("path");
|
|
15273
|
-
var fs = __require("fs"), helper = require_helper();
|
|
15325
|
+
var fs$1 = __require("fs"), helper = require_helper();
|
|
15274
15326
|
module.exports = function(connInfo, cb) {
|
|
15275
15327
|
var file = helper.getFileName();
|
|
15276
|
-
fs.stat(file, function(err, stat) {
|
|
15328
|
+
fs$1.stat(file, function(err, stat) {
|
|
15277
15329
|
if (err || !helper.usePgPass(stat, file)) return cb(void 0);
|
|
15278
|
-
var st = fs.createReadStream(file);
|
|
15330
|
+
var st = fs$1.createReadStream(file);
|
|
15279
15331
|
helper.getPassword(connInfo, st, cb);
|
|
15280
15332
|
});
|
|
15281
15333
|
};
|
|
@@ -16534,6 +16586,33 @@ var SelectionProxyHandler = class SelectionProxyHandler {
|
|
|
16534
16586
|
}
|
|
16535
16587
|
};
|
|
16536
16588
|
//#endregion
|
|
16589
|
+
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@opentelemetry+api@1.9.0_@types+better-sqlite3@7.6.13_@types+pg@8.16_374a03c8dde600dcef5a1d8002afafad/node_modules/drizzle-orm/pg-core/checks.js
|
|
16590
|
+
var CheckBuilder$1 = class {
|
|
16591
|
+
constructor(name, value) {
|
|
16592
|
+
this.name = name;
|
|
16593
|
+
this.value = value;
|
|
16594
|
+
}
|
|
16595
|
+
static [entityKind] = "PgCheckBuilder";
|
|
16596
|
+
brand;
|
|
16597
|
+
/** @internal */
|
|
16598
|
+
build(table) {
|
|
16599
|
+
return new Check$1(table, this);
|
|
16600
|
+
}
|
|
16601
|
+
};
|
|
16602
|
+
var Check$1 = class {
|
|
16603
|
+
constructor(table, builder) {
|
|
16604
|
+
this.table = table;
|
|
16605
|
+
this.name = builder.name;
|
|
16606
|
+
this.value = builder.value;
|
|
16607
|
+
}
|
|
16608
|
+
static [entityKind] = "PgCheck";
|
|
16609
|
+
name;
|
|
16610
|
+
value;
|
|
16611
|
+
};
|
|
16612
|
+
function check$1(name, value) {
|
|
16613
|
+
return new CheckBuilder$1(name, value);
|
|
16614
|
+
}
|
|
16615
|
+
//#endregion
|
|
16537
16616
|
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@opentelemetry+api@1.9.0_@types+better-sqlite3@7.6.13_@types+pg@8.16_374a03c8dde600dcef5a1d8002afafad/node_modules/drizzle-orm/pg-core/indexes.js
|
|
16538
16617
|
var IndexBuilderOn$1 = class {
|
|
16539
16618
|
constructor(unique, name) {
|
|
@@ -19335,6 +19414,20 @@ var ForeignKey = class {
|
|
|
19335
19414
|
return name ?? `${chunks.join("_")}_fk`;
|
|
19336
19415
|
}
|
|
19337
19416
|
};
|
|
19417
|
+
function foreignKey(config) {
|
|
19418
|
+
function mappedConfig() {
|
|
19419
|
+
if (typeof config === "function") {
|
|
19420
|
+
const { name, columns, foreignColumns } = config();
|
|
19421
|
+
return {
|
|
19422
|
+
name,
|
|
19423
|
+
columns,
|
|
19424
|
+
foreignColumns
|
|
19425
|
+
};
|
|
19426
|
+
}
|
|
19427
|
+
return config;
|
|
19428
|
+
}
|
|
19429
|
+
return new ForeignKeyBuilder(mappedConfig);
|
|
19430
|
+
}
|
|
19338
19431
|
//#endregion
|
|
19339
19432
|
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@opentelemetry+api@1.9.0_@types+better-sqlite3@7.6.13_@types+pg@8.16_374a03c8dde600dcef5a1d8002afafad/node_modules/drizzle-orm/sqlite-core/unique-constraint.js
|
|
19340
19433
|
function uniqueKeyName(table, columns) {
|
|
@@ -19787,6 +19880,32 @@ var sqliteTable = (name, columns, extraConfig) => {
|
|
|
19787
19880
|
return sqliteTableBase(name, columns, extraConfig);
|
|
19788
19881
|
};
|
|
19789
19882
|
//#endregion
|
|
19883
|
+
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@opentelemetry+api@1.9.0_@types+better-sqlite3@7.6.13_@types+pg@8.16_374a03c8dde600dcef5a1d8002afafad/node_modules/drizzle-orm/sqlite-core/checks.js
|
|
19884
|
+
var CheckBuilder = class {
|
|
19885
|
+
constructor(name, value) {
|
|
19886
|
+
this.name = name;
|
|
19887
|
+
this.value = value;
|
|
19888
|
+
}
|
|
19889
|
+
static [entityKind] = "SQLiteCheckBuilder";
|
|
19890
|
+
brand;
|
|
19891
|
+
build(table) {
|
|
19892
|
+
return new Check(table, this);
|
|
19893
|
+
}
|
|
19894
|
+
};
|
|
19895
|
+
var Check = class {
|
|
19896
|
+
constructor(table, builder) {
|
|
19897
|
+
this.table = table;
|
|
19898
|
+
this.name = builder.name;
|
|
19899
|
+
this.value = builder.value;
|
|
19900
|
+
}
|
|
19901
|
+
static [entityKind] = "SQLiteCheck";
|
|
19902
|
+
name;
|
|
19903
|
+
value;
|
|
19904
|
+
};
|
|
19905
|
+
function check(name, value) {
|
|
19906
|
+
return new CheckBuilder(name, value);
|
|
19907
|
+
}
|
|
19908
|
+
//#endregion
|
|
19790
19909
|
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@opentelemetry+api@1.9.0_@types+better-sqlite3@7.6.13_@types+pg@8.16_374a03c8dde600dcef5a1d8002afafad/node_modules/drizzle-orm/sqlite-core/indexes.js
|
|
19791
19910
|
var IndexBuilderOn = class {
|
|
19792
19911
|
constructor(name, unique) {
|
|
@@ -22188,7 +22307,136 @@ function drizzle(callback, batchCallback, config) {
|
|
|
22188
22307
|
return db;
|
|
22189
22308
|
}
|
|
22190
22309
|
//#endregion
|
|
22310
|
+
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@opentelemetry+api@1.9.0_@types+better-sqlite3@7.6.13_@types+pg@8.16_374a03c8dde600dcef5a1d8002afafad/node_modules/drizzle-orm/migrator.js
|
|
22311
|
+
function readMigrationFiles(config) {
|
|
22312
|
+
const migrationFolderTo = config.migrationsFolder;
|
|
22313
|
+
const migrationQueries = [];
|
|
22314
|
+
const journalPath = `${migrationFolderTo}/meta/_journal.json`;
|
|
22315
|
+
if (!fs.existsSync(journalPath)) throw new Error(`Can't find meta/_journal.json file`);
|
|
22316
|
+
const journalAsString = fs.readFileSync(`${migrationFolderTo}/meta/_journal.json`).toString();
|
|
22317
|
+
const journal = JSON.parse(journalAsString);
|
|
22318
|
+
for (const journalEntry of journal.entries) {
|
|
22319
|
+
const migrationPath = `${migrationFolderTo}/${journalEntry.tag}.sql`;
|
|
22320
|
+
try {
|
|
22321
|
+
const query = fs.readFileSync(`${migrationFolderTo}/${journalEntry.tag}.sql`).toString();
|
|
22322
|
+
const result = query.split("--> statement-breakpoint").map((it) => {
|
|
22323
|
+
return it;
|
|
22324
|
+
});
|
|
22325
|
+
migrationQueries.push({
|
|
22326
|
+
sql: result,
|
|
22327
|
+
bps: journalEntry.breakpoints,
|
|
22328
|
+
folderMillis: journalEntry.when,
|
|
22329
|
+
hash: crypto$2.createHash("sha256").update(query).digest("hex")
|
|
22330
|
+
});
|
|
22331
|
+
} catch {
|
|
22332
|
+
throw new Error(`No file ${migrationPath} found in ${migrationFolderTo} folder`);
|
|
22333
|
+
}
|
|
22334
|
+
}
|
|
22335
|
+
return migrationQueries;
|
|
22336
|
+
}
|
|
22337
|
+
//#endregion
|
|
22338
|
+
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@opentelemetry+api@1.9.0_@types+better-sqlite3@7.6.13_@types+pg@8.16_374a03c8dde600dcef5a1d8002afafad/node_modules/drizzle-orm/node-postgres/migrator.js
|
|
22339
|
+
async function migrate(db, config) {
|
|
22340
|
+
const migrations = readMigrationFiles(config);
|
|
22341
|
+
await db.dialect.migrate(migrations, db.session, config);
|
|
22342
|
+
}
|
|
22343
|
+
//#endregion
|
|
22344
|
+
//#region ../../libs/agent-daemon-state/src/migrate.ts
|
|
22345
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22346
|
+
/**
|
|
22347
|
+
* Walk up from this file to find a generated drizzle migrations folder.
|
|
22348
|
+
* Works from both `src/` (tsx dev) and `dist/src/` (compiled).
|
|
22349
|
+
*/
|
|
22350
|
+
function findMigrationsFolder(folderName) {
|
|
22351
|
+
let dir = __dirname;
|
|
22352
|
+
for (let i = 0; i < 5; i++) {
|
|
22353
|
+
const candidate = resolve(dir, folderName);
|
|
22354
|
+
if (existsSync(candidate)) return candidate;
|
|
22355
|
+
dir = dirname(dir);
|
|
22356
|
+
}
|
|
22357
|
+
return resolve(__dirname, "..", folderName);
|
|
22358
|
+
}
|
|
22359
|
+
/** Statements in a generated drizzle SQL file are joined by this marker. */
|
|
22360
|
+
var STATEMENT_BREAKPOINT = "--> statement-breakpoint";
|
|
22361
|
+
function readMigrationStatements(folder) {
|
|
22362
|
+
const files = readdirSync(folder).filter((name) => name.endsWith(".sql")).sort();
|
|
22363
|
+
const statements = [];
|
|
22364
|
+
for (const file of files) {
|
|
22365
|
+
const raw = readFileSync(resolve(folder, file), "utf8");
|
|
22366
|
+
for (const chunk of raw.split(STATEMENT_BREAKPOINT)) {
|
|
22367
|
+
const trimmed = chunk.trim();
|
|
22368
|
+
if (trimmed.length > 0) statements.push(trimmed);
|
|
22369
|
+
}
|
|
22370
|
+
}
|
|
22371
|
+
return statements;
|
|
22372
|
+
}
|
|
22373
|
+
/**
|
|
22374
|
+
* Apply the SQLite baseline synchronously via the driver's `exec`.
|
|
22375
|
+
*
|
|
22376
|
+
* Fresh-start contract: the baseline is authored to run against an EMPTY
|
|
22377
|
+
* database. The daemon-state DB is a local warm-slot cache, and this migrator
|
|
22378
|
+
* does not adopt schemas created by the previous inline `CREATE TABLE IF NOT
|
|
22379
|
+
* EXISTS` bootstrap — upgrading from that path requires deleting the local
|
|
22380
|
+
* `daemon-state.sqlite` first (or dropping the Postgres tables). This is a
|
|
22381
|
+
* deliberate scope decision: there is no durable data to preserve, so in-place
|
|
22382
|
+
* adoption of legacy schemas is intentionally not implemented.
|
|
22383
|
+
*
|
|
22384
|
+
* The daemon-state SQLite store is constructed synchronously (callers do not
|
|
22385
|
+
* await it), and the runtime driver is a `node:sqlite` `DatabaseSync` wrapped
|
|
22386
|
+
* in drizzle's sqlite-proxy — which has no file-based migrator. We therefore
|
|
22387
|
+
* apply the drizzle-kit-generated SQL ourselves: the generated file stays the
|
|
22388
|
+
* single source of truth, we just run it synchronously. Idempotent via a
|
|
22389
|
+
* tracking table so re-opening an existing DB is a no-op.
|
|
22390
|
+
*/
|
|
22391
|
+
function applySqliteMigrations(client, migrationsFolder = findMigrationsFolder("drizzle-sqlite")) {
|
|
22392
|
+
client.exec(`
|
|
22393
|
+
CREATE TABLE IF NOT EXISTS __daemon_state_migrations (
|
|
22394
|
+
statement_index INTEGER PRIMARY KEY,
|
|
22395
|
+
applied_at_ms INTEGER NOT NULL
|
|
22396
|
+
)
|
|
22397
|
+
`);
|
|
22398
|
+
const alreadyApplied = client.prepare(`SELECT COALESCE(MAX(statement_index), -1) AS last FROM __daemon_state_migrations`).get().last;
|
|
22399
|
+
const statements = readMigrationStatements(migrationsFolder);
|
|
22400
|
+
if (statements.length <= alreadyApplied + 1) return;
|
|
22401
|
+
client.exec("BEGIN IMMEDIATE");
|
|
22402
|
+
try {
|
|
22403
|
+
for (let i = alreadyApplied + 1; i < statements.length; i++) {
|
|
22404
|
+
client.exec(statements[i]);
|
|
22405
|
+
client.prepare(`INSERT INTO __daemon_state_migrations (statement_index, applied_at_ms)
|
|
22406
|
+
VALUES (?, ?)`).run(i, Date.now());
|
|
22407
|
+
}
|
|
22408
|
+
client.exec("COMMIT");
|
|
22409
|
+
} catch (error) {
|
|
22410
|
+
try {
|
|
22411
|
+
client.exec("ROLLBACK");
|
|
22412
|
+
} catch {}
|
|
22413
|
+
throw error;
|
|
22414
|
+
}
|
|
22415
|
+
}
|
|
22416
|
+
/**
|
|
22417
|
+
* Run the Postgres baseline via the standard node-postgres migrator (tracked,
|
|
22418
|
+
* transactional, idempotent). Mirrors `libs/database/src/migrate.ts`. Used when
|
|
22419
|
+
* the daemon points at a shared Postgres instance instead of a local SQLite
|
|
22420
|
+
* file.
|
|
22421
|
+
*/
|
|
22422
|
+
async function runPgMigrations(pool) {
|
|
22423
|
+
await migrate(drizzle$1(pool), { migrationsFolder: findMigrationsFolder("drizzle-pg") });
|
|
22424
|
+
}
|
|
22425
|
+
//#endregion
|
|
22191
22426
|
//#region ../../libs/agent-daemon-state/src/pg-schema.ts
|
|
22427
|
+
var pgDaemonWorkspaces = pgTable("daemon_workspaces", {
|
|
22428
|
+
workspaceId: text$1("workspace_id").primaryKey(),
|
|
22429
|
+
worktreePath: text$1("worktree_path").notNull(),
|
|
22430
|
+
worktreeBranch: text$1("worktree_branch"),
|
|
22431
|
+
kind: text$1("kind", { enum: [
|
|
22432
|
+
"origin",
|
|
22433
|
+
"fork",
|
|
22434
|
+
"scratch"
|
|
22435
|
+
] }).notNull(),
|
|
22436
|
+
refcount: integer$1("refcount").notNull().default(0),
|
|
22437
|
+
createdAtMs: bigint("created_at_ms", { mode: "number" }).notNull(),
|
|
22438
|
+
lastUsedAtMs: bigint("last_used_at_ms", { mode: "number" }).notNull()
|
|
22439
|
+
}, (table) => [check$1("daemon_workspaces_kind_check", sql`${table.kind} IN ('origin', 'fork', 'scratch')`)]);
|
|
22192
22440
|
var pgDaemonSlots = pgTable("daemon_slots", {
|
|
22193
22441
|
agentName: text$1("agent_name").notNull(),
|
|
22194
22442
|
provider: text$1("provider").notNull(),
|
|
@@ -22198,6 +22446,7 @@ var pgDaemonSlots = pgTable("daemon_slots", {
|
|
|
22198
22446
|
state: text$1("state", { enum: ["active", "idle"] }).notNull(),
|
|
22199
22447
|
lastTaskId: text$1("last_task_id").notNull(),
|
|
22200
22448
|
lastAttemptN: integer$1("last_attempt_n").notNull(),
|
|
22449
|
+
workspaceId: text$1("workspace_id").references(() => pgDaemonWorkspaces.workspaceId, { onDelete: "set null" }),
|
|
22201
22450
|
createdAtMs: bigint("created_at_ms", { mode: "number" }).notNull(),
|
|
22202
22451
|
lastUsedAtMs: bigint("last_used_at_ms", { mode: "number" }).notNull(),
|
|
22203
22452
|
expiresAtMs: bigint("expires_at_ms", { mode: "number" }).notNull()
|
|
@@ -22208,8 +22457,9 @@ var pgDaemonSlots = pgTable("daemon_slots", {
|
|
|
22208
22457
|
table.model,
|
|
22209
22458
|
table.slotKey
|
|
22210
22459
|
] }),
|
|
22460
|
+
check$1("daemon_slots_state_check", sql`${table.state} IN ('active', 'idle')`),
|
|
22211
22461
|
index$1("daemon_slots_expires_idx").on(table.expiresAtMs),
|
|
22212
|
-
index$1("daemon_slots_task_attempt_idx").on(table.lastTaskId, table.lastAttemptN, table.lastUsedAtMs)
|
|
22462
|
+
index$1("daemon_slots_task_attempt_idx").on(table.lastTaskId, table.lastAttemptN, table.lastUsedAtMs.desc())
|
|
22213
22463
|
]);
|
|
22214
22464
|
var pgDaemonSlotSessions = pgTable("daemon_slot_sessions", {
|
|
22215
22465
|
agentName: text$1("agent_name").notNull(),
|
|
@@ -22223,28 +22473,40 @@ var pgDaemonSlotSessions = pgTable("daemon_slot_sessions", {
|
|
|
22223
22473
|
table.provider,
|
|
22224
22474
|
table.model,
|
|
22225
22475
|
table.slotKey
|
|
22226
|
-
] })
|
|
22227
|
-
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
|
|
22231
|
-
|
|
22232
|
-
|
|
22233
|
-
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
] })]);
|
|
22476
|
+
] }), foreignKey$1({
|
|
22477
|
+
columns: [
|
|
22478
|
+
table.agentName,
|
|
22479
|
+
table.provider,
|
|
22480
|
+
table.model,
|
|
22481
|
+
table.slotKey
|
|
22482
|
+
],
|
|
22483
|
+
foreignColumns: [
|
|
22484
|
+
pgDaemonSlots.agentName,
|
|
22485
|
+
pgDaemonSlots.provider,
|
|
22486
|
+
pgDaemonSlots.model,
|
|
22487
|
+
pgDaemonSlots.slotKey
|
|
22488
|
+
]
|
|
22489
|
+
}).onDelete("cascade")]);
|
|
22241
22490
|
var pgAgentDaemonStateSchema = {
|
|
22242
22491
|
daemonSlotSessions: pgDaemonSlotSessions,
|
|
22243
|
-
|
|
22492
|
+
daemonWorkspaces: pgDaemonWorkspaces,
|
|
22244
22493
|
daemonSlots: pgDaemonSlots
|
|
22245
22494
|
};
|
|
22246
22495
|
//#endregion
|
|
22247
22496
|
//#region ../../libs/agent-daemon-state/src/schema.ts
|
|
22497
|
+
var daemonWorkspaces = sqliteTable("daemon_workspaces", {
|
|
22498
|
+
workspaceId: text("workspace_id").primaryKey(),
|
|
22499
|
+
worktreePath: text("worktree_path").notNull(),
|
|
22500
|
+
worktreeBranch: text("worktree_branch"),
|
|
22501
|
+
kind: text("kind", { enum: [
|
|
22502
|
+
"origin",
|
|
22503
|
+
"fork",
|
|
22504
|
+
"scratch"
|
|
22505
|
+
] }).notNull(),
|
|
22506
|
+
refcount: integer("refcount").notNull().default(0),
|
|
22507
|
+
createdAtMs: integer("created_at_ms").notNull(),
|
|
22508
|
+
lastUsedAtMs: integer("last_used_at_ms").notNull()
|
|
22509
|
+
}, (table) => [check("daemon_workspaces_kind_check", sql`${table.kind} IN ('origin', 'fork', 'scratch')`)]);
|
|
22248
22510
|
var daemonSlots = sqliteTable("daemon_slots", {
|
|
22249
22511
|
agentName: text("agent_name").notNull(),
|
|
22250
22512
|
provider: text("provider").notNull(),
|
|
@@ -22254,6 +22516,7 @@ var daemonSlots = sqliteTable("daemon_slots", {
|
|
|
22254
22516
|
state: text("state", { enum: ["active", "idle"] }).notNull(),
|
|
22255
22517
|
lastTaskId: text("last_task_id").notNull(),
|
|
22256
22518
|
lastAttemptN: integer("last_attempt_n").notNull(),
|
|
22519
|
+
workspaceId: text("workspace_id").references(() => daemonWorkspaces.workspaceId, { onDelete: "set null" }),
|
|
22257
22520
|
createdAtMs: integer("created_at_ms").notNull(),
|
|
22258
22521
|
lastUsedAtMs: integer("last_used_at_ms").notNull(),
|
|
22259
22522
|
expiresAtMs: integer("expires_at_ms").notNull()
|
|
@@ -22264,8 +22527,9 @@ var daemonSlots = sqliteTable("daemon_slots", {
|
|
|
22264
22527
|
table.model,
|
|
22265
22528
|
table.slotKey
|
|
22266
22529
|
] }),
|
|
22530
|
+
check("daemon_slots_state_check", sql`${table.state} IN ('active', 'idle')`),
|
|
22267
22531
|
index("daemon_slots_expires_idx").on(table.expiresAtMs),
|
|
22268
|
-
index("daemon_slots_task_attempt_idx").on(table.lastTaskId, table.lastAttemptN, table.lastUsedAtMs)
|
|
22532
|
+
index("daemon_slots_task_attempt_idx").on(table.lastTaskId, table.lastAttemptN, sql`${table.lastUsedAtMs} DESC`)
|
|
22269
22533
|
]);
|
|
22270
22534
|
var daemonSlotSessions = sqliteTable("daemon_slot_sessions", {
|
|
22271
22535
|
agentName: text("agent_name").notNull(),
|
|
@@ -22279,24 +22543,23 @@ var daemonSlotSessions = sqliteTable("daemon_slot_sessions", {
|
|
|
22279
22543
|
table.provider,
|
|
22280
22544
|
table.model,
|
|
22281
22545
|
table.slotKey
|
|
22282
|
-
] })
|
|
22283
|
-
|
|
22284
|
-
|
|
22285
|
-
|
|
22286
|
-
|
|
22287
|
-
|
|
22288
|
-
|
|
22289
|
-
|
|
22290
|
-
|
|
22291
|
-
|
|
22292
|
-
|
|
22293
|
-
|
|
22294
|
-
|
|
22295
|
-
|
|
22296
|
-
] })]);
|
|
22546
|
+
] }), foreignKey({
|
|
22547
|
+
columns: [
|
|
22548
|
+
table.agentName,
|
|
22549
|
+
table.provider,
|
|
22550
|
+
table.model,
|
|
22551
|
+
table.slotKey
|
|
22552
|
+
],
|
|
22553
|
+
foreignColumns: [
|
|
22554
|
+
daemonSlots.agentName,
|
|
22555
|
+
daemonSlots.provider,
|
|
22556
|
+
daemonSlots.model,
|
|
22557
|
+
daemonSlots.slotKey
|
|
22558
|
+
]
|
|
22559
|
+
}).onDelete("cascade")]);
|
|
22297
22560
|
var agentDaemonStateSchema = {
|
|
22298
22561
|
daemonSlotSessions,
|
|
22299
|
-
|
|
22562
|
+
daemonWorkspaces,
|
|
22300
22563
|
daemonSlots
|
|
22301
22564
|
};
|
|
22302
22565
|
//#endregion
|
|
@@ -22339,59 +22602,7 @@ var SqliteDaemonSlotStore = class {
|
|
|
22339
22602
|
this.withSyncDb("initialize sqlite schema", () => {
|
|
22340
22603
|
this.client.exec("PRAGMA journal_mode = WAL");
|
|
22341
22604
|
this.client.exec("PRAGMA foreign_keys = ON");
|
|
22342
|
-
this.client
|
|
22343
|
-
CREATE TABLE IF NOT EXISTS daemon_slots (
|
|
22344
|
-
agent_name TEXT NOT NULL,
|
|
22345
|
-
provider TEXT NOT NULL,
|
|
22346
|
-
model TEXT NOT NULL,
|
|
22347
|
-
slot_key TEXT NOT NULL,
|
|
22348
|
-
task_type TEXT NOT NULL,
|
|
22349
|
-
state TEXT NOT NULL CHECK (state IN ('active', 'idle')),
|
|
22350
|
-
last_task_id TEXT NOT NULL,
|
|
22351
|
-
last_attempt_n INTEGER NOT NULL,
|
|
22352
|
-
created_at_ms INTEGER NOT NULL,
|
|
22353
|
-
last_used_at_ms INTEGER NOT NULL,
|
|
22354
|
-
expires_at_ms INTEGER NOT NULL,
|
|
22355
|
-
PRIMARY KEY (agent_name, provider, model, slot_key)
|
|
22356
|
-
);
|
|
22357
|
-
|
|
22358
|
-
CREATE TABLE IF NOT EXISTS daemon_slot_sessions (
|
|
22359
|
-
agent_name TEXT NOT NULL,
|
|
22360
|
-
provider TEXT NOT NULL,
|
|
22361
|
-
model TEXT NOT NULL,
|
|
22362
|
-
slot_key TEXT NOT NULL,
|
|
22363
|
-
session_dir TEXT NOT NULL UNIQUE,
|
|
22364
|
-
session_path TEXT,
|
|
22365
|
-
PRIMARY KEY (agent_name, provider, model, slot_key),
|
|
22366
|
-
FOREIGN KEY (agent_name, provider, model, slot_key)
|
|
22367
|
-
REFERENCES daemon_slots(agent_name, provider, model, slot_key)
|
|
22368
|
-
ON DELETE CASCADE
|
|
22369
|
-
);
|
|
22370
|
-
|
|
22371
|
-
CREATE TABLE IF NOT EXISTS daemon_slot_workspaces (
|
|
22372
|
-
agent_name TEXT NOT NULL,
|
|
22373
|
-
provider TEXT NOT NULL,
|
|
22374
|
-
model TEXT NOT NULL,
|
|
22375
|
-
slot_key TEXT NOT NULL,
|
|
22376
|
-
workspace_id TEXT NOT NULL UNIQUE,
|
|
22377
|
-
worktree_path TEXT NOT NULL,
|
|
22378
|
-
worktree_branch TEXT,
|
|
22379
|
-
PRIMARY KEY (agent_name, provider, model, slot_key),
|
|
22380
|
-
FOREIGN KEY (agent_name, provider, model, slot_key)
|
|
22381
|
-
REFERENCES daemon_slots(agent_name, provider, model, slot_key)
|
|
22382
|
-
ON DELETE CASCADE
|
|
22383
|
-
);
|
|
22384
|
-
|
|
22385
|
-
CREATE INDEX IF NOT EXISTS daemon_slots_expires_idx
|
|
22386
|
-
ON daemon_slots (expires_at_ms);
|
|
22387
|
-
|
|
22388
|
-
CREATE INDEX IF NOT EXISTS daemon_slots_task_attempt_idx
|
|
22389
|
-
ON daemon_slots (
|
|
22390
|
-
last_task_id,
|
|
22391
|
-
last_attempt_n,
|
|
22392
|
-
last_used_at_ms DESC
|
|
22393
|
-
);
|
|
22394
|
-
`);
|
|
22605
|
+
applySqliteMigrations(this.client);
|
|
22395
22606
|
});
|
|
22396
22607
|
} catch (error) {
|
|
22397
22608
|
throw new DaemonSlotRegistryError("open sqlite database", error);
|
|
@@ -22403,34 +22614,45 @@ var SqliteDaemonSlotStore = class {
|
|
|
22403
22614
|
async beginSlot(input) {
|
|
22404
22615
|
const now = Date.now();
|
|
22405
22616
|
const expiresAtMs = now + input.ttlSec * 1e3;
|
|
22406
|
-
|
|
22407
|
-
|
|
22408
|
-
|
|
22409
|
-
|
|
22410
|
-
|
|
22411
|
-
|
|
22412
|
-
|
|
22413
|
-
|
|
22414
|
-
|
|
22415
|
-
|
|
22416
|
-
|
|
22417
|
-
|
|
22418
|
-
|
|
22419
|
-
|
|
22420
|
-
|
|
22421
|
-
|
|
22422
|
-
|
|
22423
|
-
|
|
22424
|
-
|
|
22425
|
-
|
|
22426
|
-
|
|
22427
|
-
|
|
22428
|
-
|
|
22429
|
-
|
|
22430
|
-
|
|
22431
|
-
|
|
22432
|
-
|
|
22433
|
-
|
|
22617
|
+
const workspaceId = input.workspaceId !== null && input.worktreePath !== null ? input.workspaceId : null;
|
|
22618
|
+
this.withSyncDb("upsert sqlite slot + workspace", () => {
|
|
22619
|
+
this.client.exec("BEGIN IMMEDIATE");
|
|
22620
|
+
try {
|
|
22621
|
+
const priorWorkspaceId = this.priorSlotWorkspaceId(input);
|
|
22622
|
+
if (workspaceId !== null) this.client.prepare(`INSERT INTO daemon_workspaces
|
|
22623
|
+
(workspace_id, worktree_path, worktree_branch, kind,
|
|
22624
|
+
refcount, created_at_ms, last_used_at_ms)
|
|
22625
|
+
VALUES (?, ?, ?, ?, 0, ?, ?)
|
|
22626
|
+
ON CONFLICT(workspace_id) DO UPDATE SET
|
|
22627
|
+
worktree_path = excluded.worktree_path,
|
|
22628
|
+
worktree_branch = excluded.worktree_branch,
|
|
22629
|
+
last_used_at_ms = excluded.last_used_at_ms`).run(workspaceId, input.worktreePath, input.worktreeBranch, input.workspaceKind ?? "origin", now, now);
|
|
22630
|
+
this.client.prepare(`INSERT INTO daemon_slots
|
|
22631
|
+
(agent_name, provider, model, slot_key, task_type, state,
|
|
22632
|
+
last_task_id, last_attempt_n, workspace_id, created_at_ms,
|
|
22633
|
+
last_used_at_ms, expires_at_ms)
|
|
22634
|
+
VALUES (?, ?, ?, ?, ?, 'active', ?, ?, ?, ?, ?, ?)
|
|
22635
|
+
ON CONFLICT(agent_name, provider, model, slot_key) DO UPDATE SET
|
|
22636
|
+
task_type = excluded.task_type,
|
|
22637
|
+
state = 'active',
|
|
22638
|
+
last_task_id = excluded.last_task_id,
|
|
22639
|
+
last_attempt_n = excluded.last_attempt_n,
|
|
22640
|
+
workspace_id = excluded.workspace_id,
|
|
22641
|
+
last_used_at_ms = excluded.last_used_at_ms,
|
|
22642
|
+
expires_at_ms = excluded.expires_at_ms`).run(input.agentName, input.provider, input.model, input.slotKey, input.taskType, input.lastTaskId, input.lastAttemptN, workspaceId, now, now, expiresAtMs);
|
|
22643
|
+
if (priorWorkspaceId !== workspaceId) {
|
|
22644
|
+
if (priorWorkspaceId !== null) this.decrementWorkspaceRefcount(priorWorkspaceId);
|
|
22645
|
+
if (workspaceId !== null) this.client.prepare(`UPDATE daemon_workspaces SET refcount = refcount + 1
|
|
22646
|
+
WHERE workspace_id = ?`).run(workspaceId);
|
|
22647
|
+
}
|
|
22648
|
+
this.client.exec("COMMIT");
|
|
22649
|
+
} catch (error) {
|
|
22650
|
+
try {
|
|
22651
|
+
this.client.exec("ROLLBACK");
|
|
22652
|
+
} catch {}
|
|
22653
|
+
throw error;
|
|
22654
|
+
}
|
|
22655
|
+
});
|
|
22434
22656
|
const sessionDir = input.sessionDir;
|
|
22435
22657
|
if (sessionDir !== null) await this.withDb("upsert sqlite slot session", () => this.db.insert(daemonSlotSessions).values({
|
|
22436
22658
|
agentName: input.agentName,
|
|
@@ -22451,29 +22673,17 @@ var SqliteDaemonSlotStore = class {
|
|
|
22451
22673
|
daemonSlotSessions.slotKey
|
|
22452
22674
|
]
|
|
22453
22675
|
}).run());
|
|
22454
|
-
|
|
22455
|
-
|
|
22456
|
-
|
|
22457
|
-
|
|
22458
|
-
|
|
22459
|
-
|
|
22460
|
-
|
|
22461
|
-
|
|
22462
|
-
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
set: {
|
|
22466
|
-
workspaceId: sql`excluded.workspace_id`,
|
|
22467
|
-
worktreeBranch: sql`excluded.worktree_branch`,
|
|
22468
|
-
worktreePath: sql`excluded.worktree_path`
|
|
22469
|
-
},
|
|
22470
|
-
target: [
|
|
22471
|
-
daemonSlotWorkspaces.agentName,
|
|
22472
|
-
daemonSlotWorkspaces.provider,
|
|
22473
|
-
daemonSlotWorkspaces.model,
|
|
22474
|
-
daemonSlotWorkspaces.slotKey
|
|
22475
|
-
]
|
|
22476
|
-
}).run());
|
|
22676
|
+
}
|
|
22677
|
+
/** The workspace_id the slot referenced before this begin, or null. */
|
|
22678
|
+
priorSlotWorkspaceId(slot) {
|
|
22679
|
+
return this.client.prepare(`SELECT workspace_id AS workspaceId FROM daemon_slots
|
|
22680
|
+
WHERE agent_name = ? AND provider = ? AND model = ? AND slot_key = ?`).get(slot.agentName, slot.provider, slot.model, slot.slotKey)?.workspaceId ?? null;
|
|
22681
|
+
}
|
|
22682
|
+
/** Decrement a workspace refcount; delete the row when it reaches 0. */
|
|
22683
|
+
decrementWorkspaceRefcount(workspaceId) {
|
|
22684
|
+
this.client.prepare(`UPDATE daemon_workspaces SET refcount = refcount - 1
|
|
22685
|
+
WHERE workspace_id = ?`).run(workspaceId);
|
|
22686
|
+
this.client.prepare(`DELETE FROM daemon_workspaces WHERE workspace_id = ? AND refcount <= 0`).run(workspaceId);
|
|
22477
22687
|
}
|
|
22478
22688
|
async finishSlot(identity, slotKey, ttlSec, sessionPath) {
|
|
22479
22689
|
const now = Date.now();
|
|
@@ -22502,13 +22712,27 @@ var SqliteDaemonSlotStore = class {
|
|
|
22502
22712
|
slot,
|
|
22503
22713
|
workspace: await this.lookupWorkspace(slot)
|
|
22504
22714
|
});
|
|
22505
|
-
this.withSyncDb("delete expired sqlite slots", () => {
|
|
22715
|
+
const releasable = this.withSyncDb("delete expired sqlite slots", () => {
|
|
22506
22716
|
this.client.exec("BEGIN IMMEDIATE");
|
|
22507
22717
|
try {
|
|
22508
|
-
|
|
22509
|
-
|
|
22510
|
-
|
|
22718
|
+
const orphaned = [];
|
|
22719
|
+
for (const reaped of out) {
|
|
22720
|
+
this.client.prepare(`DELETE FROM daemon_slots
|
|
22721
|
+
WHERE agent_name = ? AND provider = ? AND model = ?
|
|
22722
|
+
AND slot_key = ?`).run(reaped.slot.agentName, reaped.slot.provider, reaped.slot.model, reaped.slot.slotKey);
|
|
22723
|
+
const ws = reaped.workspace;
|
|
22724
|
+
if (ws) {
|
|
22725
|
+
this.client.prepare(`UPDATE daemon_workspaces SET refcount = refcount - 1
|
|
22726
|
+
WHERE workspace_id = ?`).run(ws.workspaceId);
|
|
22727
|
+
const row = this.client.prepare(`SELECT refcount FROM daemon_workspaces WHERE workspace_id = ?`).get(ws.workspaceId);
|
|
22728
|
+
if (!row || row.refcount <= 0) {
|
|
22729
|
+
this.client.prepare(`DELETE FROM daemon_workspaces WHERE workspace_id = ?`).run(ws.workspaceId);
|
|
22730
|
+
orphaned.push(ws);
|
|
22731
|
+
}
|
|
22732
|
+
}
|
|
22733
|
+
}
|
|
22511
22734
|
this.client.exec("COMMIT");
|
|
22735
|
+
return orphaned;
|
|
22512
22736
|
} catch (error) {
|
|
22513
22737
|
try {
|
|
22514
22738
|
this.client.exec("ROLLBACK");
|
|
@@ -22516,7 +22740,9 @@ var SqliteDaemonSlotStore = class {
|
|
|
22516
22740
|
throw error;
|
|
22517
22741
|
}
|
|
22518
22742
|
});
|
|
22519
|
-
|
|
22743
|
+
cleanupReapedSessions(out);
|
|
22744
|
+
cleanupReleasableWorkspaces(releasable);
|
|
22745
|
+
return out;
|
|
22520
22746
|
} catch (error) {
|
|
22521
22747
|
throw error instanceof DaemonSlotRegistryError ? error : new DaemonSlotRegistryError("reap expired sqlite slots", error);
|
|
22522
22748
|
}
|
|
@@ -22525,7 +22751,8 @@ var SqliteDaemonSlotStore = class {
|
|
|
22525
22751
|
return this.withDb("select sqlite slot session", async () => await this.db.select().from(daemonSlotSessions).where(sqliteSlotSessionIdentityWhere(slot, slot.slotKey)).get() ?? null);
|
|
22526
22752
|
}
|
|
22527
22753
|
async lookupWorkspace(slot) {
|
|
22528
|
-
|
|
22754
|
+
if (!slot.workspaceId) return null;
|
|
22755
|
+
return this.withDb("select sqlite workspace", async () => await this.db.select().from(daemonWorkspaces).where(eq(daemonWorkspaces.workspaceId, slot.workspaceId)).get() ?? null);
|
|
22529
22756
|
}
|
|
22530
22757
|
executeProxyQuery(query, params, method) {
|
|
22531
22758
|
return Promise.resolve(this.withSyncDb("execute sqlite proxy query", () => {
|
|
@@ -22575,34 +22802,64 @@ var PgDaemonSlotStore = class {
|
|
|
22575
22802
|
await this.ready;
|
|
22576
22803
|
const now = Date.now();
|
|
22577
22804
|
const expiresAtMs = now + input.ttlSec * 1e3;
|
|
22578
|
-
|
|
22579
|
-
|
|
22580
|
-
|
|
22581
|
-
|
|
22582
|
-
|
|
22583
|
-
|
|
22584
|
-
|
|
22585
|
-
|
|
22586
|
-
|
|
22587
|
-
|
|
22588
|
-
|
|
22589
|
-
|
|
22590
|
-
|
|
22591
|
-
|
|
22592
|
-
|
|
22593
|
-
|
|
22594
|
-
|
|
22595
|
-
|
|
22805
|
+
const beginWorkspaceId = input.workspaceId !== null && input.worktreePath !== null ? input.workspaceId : null;
|
|
22806
|
+
await this.withDb("upsert postgres slot + workspace", () => this.db.transaction(async (tx) => {
|
|
22807
|
+
const [priorRow] = await tx.select({ workspaceId: pgDaemonSlots.workspaceId }).from(pgDaemonSlots).where(pgSlotIdentityWhere(input, input.slotKey)).limit(1).execute();
|
|
22808
|
+
const priorWorkspaceId = priorRow?.workspaceId ?? null;
|
|
22809
|
+
if (beginWorkspaceId !== null) await tx.insert(pgDaemonWorkspaces).values({
|
|
22810
|
+
workspaceId: beginWorkspaceId,
|
|
22811
|
+
worktreePath: input.worktreePath,
|
|
22812
|
+
worktreeBranch: input.worktreeBranch,
|
|
22813
|
+
kind: input.workspaceKind ?? "origin",
|
|
22814
|
+
refcount: 0,
|
|
22815
|
+
createdAtMs: now,
|
|
22816
|
+
lastUsedAtMs: now
|
|
22817
|
+
}).onConflictDoUpdate({
|
|
22818
|
+
set: {
|
|
22819
|
+
worktreePath: sql`excluded.worktree_path`,
|
|
22820
|
+
worktreeBranch: sql`excluded.worktree_branch`,
|
|
22821
|
+
lastUsedAtMs: sql`excluded.last_used_at_ms`
|
|
22822
|
+
},
|
|
22823
|
+
target: [pgDaemonWorkspaces.workspaceId]
|
|
22824
|
+
}).execute();
|
|
22825
|
+
await tx.insert(pgDaemonSlots).values({
|
|
22826
|
+
agentName: input.agentName,
|
|
22827
|
+
createdAtMs: now,
|
|
22828
|
+
expiresAtMs,
|
|
22829
|
+
lastAttemptN: input.lastAttemptN,
|
|
22830
|
+
lastTaskId: input.lastTaskId,
|
|
22831
|
+
lastUsedAtMs: now,
|
|
22832
|
+
model: input.model,
|
|
22833
|
+
provider: input.provider,
|
|
22834
|
+
slotKey: input.slotKey,
|
|
22596
22835
|
state: "active",
|
|
22597
|
-
taskType:
|
|
22598
|
-
|
|
22599
|
-
|
|
22600
|
-
|
|
22601
|
-
|
|
22602
|
-
|
|
22603
|
-
|
|
22604
|
-
|
|
22605
|
-
|
|
22836
|
+
taskType: input.taskType,
|
|
22837
|
+
workspaceId: beginWorkspaceId
|
|
22838
|
+
}).onConflictDoUpdate({
|
|
22839
|
+
set: {
|
|
22840
|
+
expiresAtMs: sql`excluded.expires_at_ms`,
|
|
22841
|
+
lastAttemptN: sql`excluded.last_attempt_n`,
|
|
22842
|
+
lastTaskId: sql`excluded.last_task_id`,
|
|
22843
|
+
lastUsedAtMs: sql`excluded.last_used_at_ms`,
|
|
22844
|
+
state: "active",
|
|
22845
|
+
taskType: sql`excluded.task_type`,
|
|
22846
|
+
workspaceId: sql`excluded.workspace_id`
|
|
22847
|
+
},
|
|
22848
|
+
target: [
|
|
22849
|
+
pgDaemonSlots.agentName,
|
|
22850
|
+
pgDaemonSlots.provider,
|
|
22851
|
+
pgDaemonSlots.model,
|
|
22852
|
+
pgDaemonSlots.slotKey
|
|
22853
|
+
]
|
|
22854
|
+
}).execute();
|
|
22855
|
+
if (priorWorkspaceId !== beginWorkspaceId) {
|
|
22856
|
+
if (priorWorkspaceId !== null) {
|
|
22857
|
+
await tx.update(pgDaemonWorkspaces).set({ refcount: sql`${pgDaemonWorkspaces.refcount} - 1` }).where(eq(pgDaemonWorkspaces.workspaceId, priorWorkspaceId)).execute();
|
|
22858
|
+
await tx.delete(pgDaemonWorkspaces).where(and(eq(pgDaemonWorkspaces.workspaceId, priorWorkspaceId), lte(pgDaemonWorkspaces.refcount, 0))).execute();
|
|
22859
|
+
}
|
|
22860
|
+
if (beginWorkspaceId !== null) await tx.update(pgDaemonWorkspaces).set({ refcount: sql`${pgDaemonWorkspaces.refcount} + 1` }).where(eq(pgDaemonWorkspaces.workspaceId, beginWorkspaceId)).execute();
|
|
22861
|
+
}
|
|
22862
|
+
}));
|
|
22606
22863
|
const sessionDir = input.sessionDir;
|
|
22607
22864
|
if (sessionDir !== null) await this.withDb("upsert postgres slot session", () => this.db.insert(pgDaemonSlotSessions).values({
|
|
22608
22865
|
agentName: input.agentName,
|
|
@@ -22623,29 +22880,6 @@ var PgDaemonSlotStore = class {
|
|
|
22623
22880
|
pgDaemonSlotSessions.slotKey
|
|
22624
22881
|
]
|
|
22625
22882
|
}).execute());
|
|
22626
|
-
const workspaceId = input.workspaceId;
|
|
22627
|
-
const worktreePath = input.worktreePath;
|
|
22628
|
-
if (workspaceId !== null && worktreePath !== null) await this.withDb("upsert postgres slot workspace", () => this.db.insert(pgDaemonSlotWorkspaces).values({
|
|
22629
|
-
agentName: input.agentName,
|
|
22630
|
-
model: input.model,
|
|
22631
|
-
provider: input.provider,
|
|
22632
|
-
slotKey: input.slotKey,
|
|
22633
|
-
workspaceId,
|
|
22634
|
-
worktreeBranch: input.worktreeBranch,
|
|
22635
|
-
worktreePath
|
|
22636
|
-
}).onConflictDoUpdate({
|
|
22637
|
-
set: {
|
|
22638
|
-
workspaceId: sql`excluded.workspace_id`,
|
|
22639
|
-
worktreeBranch: sql`excluded.worktree_branch`,
|
|
22640
|
-
worktreePath: sql`excluded.worktree_path`
|
|
22641
|
-
},
|
|
22642
|
-
target: [
|
|
22643
|
-
pgDaemonSlotWorkspaces.agentName,
|
|
22644
|
-
pgDaemonSlotWorkspaces.provider,
|
|
22645
|
-
pgDaemonSlotWorkspaces.model,
|
|
22646
|
-
pgDaemonSlotWorkspaces.slotKey
|
|
22647
|
-
]
|
|
22648
|
-
}).execute());
|
|
22649
22883
|
}
|
|
22650
22884
|
async finishSlot(identity, slotKey, ttlSec, sessionPath) {
|
|
22651
22885
|
await this.ready;
|
|
@@ -22670,91 +22904,50 @@ var PgDaemonSlotStore = class {
|
|
|
22670
22904
|
async reapExpiredSlots(now = Date.now()) {
|
|
22671
22905
|
await this.ready;
|
|
22672
22906
|
try {
|
|
22673
|
-
|
|
22907
|
+
const { out, releasable } = await this.db.transaction(async (tx) => {
|
|
22674
22908
|
const slots = await tx.select().from(pgDaemonSlots).where(lte(pgDaemonSlots.expiresAtMs, now)).execute();
|
|
22675
22909
|
const reaped = [];
|
|
22910
|
+
const orphaned = [];
|
|
22676
22911
|
for (const slot of slots) {
|
|
22677
22912
|
const [session = null] = await tx.select().from(pgDaemonSlotSessions).where(pgSlotSessionIdentityWhere(slot, slot.slotKey)).limit(1).execute();
|
|
22678
|
-
const
|
|
22913
|
+
const workspace = slot.workspaceId ? (await tx.select().from(pgDaemonWorkspaces).where(eq(pgDaemonWorkspaces.workspaceId, slot.workspaceId)).limit(1).execute())[0] ?? null : null;
|
|
22679
22914
|
reaped.push({
|
|
22680
22915
|
session,
|
|
22681
22916
|
slot,
|
|
22682
22917
|
workspace
|
|
22683
22918
|
});
|
|
22684
22919
|
await tx.delete(pgDaemonSlots).where(pgSlotIdentityWhere(slot, slot.slotKey)).execute();
|
|
22920
|
+
if (workspace) {
|
|
22921
|
+
await tx.update(pgDaemonWorkspaces).set({ refcount: sql`${pgDaemonWorkspaces.refcount} - 1` }).where(eq(pgDaemonWorkspaces.workspaceId, workspace.workspaceId)).execute();
|
|
22922
|
+
const [after] = await tx.select({ refcount: pgDaemonWorkspaces.refcount }).from(pgDaemonWorkspaces).where(eq(pgDaemonWorkspaces.workspaceId, workspace.workspaceId)).limit(1).execute();
|
|
22923
|
+
if (!after || after.refcount <= 0) {
|
|
22924
|
+
await tx.delete(pgDaemonWorkspaces).where(eq(pgDaemonWorkspaces.workspaceId, workspace.workspaceId)).execute();
|
|
22925
|
+
orphaned.push(workspace);
|
|
22926
|
+
}
|
|
22927
|
+
}
|
|
22685
22928
|
}
|
|
22686
|
-
return
|
|
22687
|
-
|
|
22929
|
+
return {
|
|
22930
|
+
out: reaped,
|
|
22931
|
+
releasable: orphaned
|
|
22932
|
+
};
|
|
22933
|
+
});
|
|
22934
|
+
cleanupReapedSessions(out);
|
|
22935
|
+
cleanupReleasableWorkspaces(releasable);
|
|
22936
|
+
return out;
|
|
22688
22937
|
} catch (error) {
|
|
22689
22938
|
throw error instanceof DaemonSlotRegistryError ? error : new DaemonSlotRegistryError("reap expired postgres slots", error);
|
|
22690
22939
|
}
|
|
22691
22940
|
}
|
|
22692
22941
|
async initialize() {
|
|
22693
|
-
await this.withDb("initialize postgres schema", () => this.pool
|
|
22694
|
-
CREATE TABLE IF NOT EXISTS daemon_slots (
|
|
22695
|
-
agent_name TEXT NOT NULL,
|
|
22696
|
-
provider TEXT NOT NULL,
|
|
22697
|
-
model TEXT NOT NULL,
|
|
22698
|
-
slot_key TEXT NOT NULL,
|
|
22699
|
-
task_type TEXT NOT NULL,
|
|
22700
|
-
state TEXT NOT NULL CHECK (state IN ('active', 'idle')),
|
|
22701
|
-
last_task_id TEXT NOT NULL,
|
|
22702
|
-
last_attempt_n INTEGER NOT NULL,
|
|
22703
|
-
created_at_ms BIGINT NOT NULL,
|
|
22704
|
-
last_used_at_ms BIGINT NOT NULL,
|
|
22705
|
-
expires_at_ms BIGINT NOT NULL,
|
|
22706
|
-
PRIMARY KEY (agent_name, provider, model, slot_key)
|
|
22707
|
-
);
|
|
22708
|
-
|
|
22709
|
-
CREATE TABLE IF NOT EXISTS daemon_slot_sessions (
|
|
22710
|
-
agent_name TEXT NOT NULL,
|
|
22711
|
-
provider TEXT NOT NULL,
|
|
22712
|
-
model TEXT NOT NULL,
|
|
22713
|
-
slot_key TEXT NOT NULL,
|
|
22714
|
-
session_dir TEXT NOT NULL UNIQUE,
|
|
22715
|
-
session_path TEXT,
|
|
22716
|
-
PRIMARY KEY (agent_name, provider, model, slot_key),
|
|
22717
|
-
FOREIGN KEY (agent_name, provider, model, slot_key)
|
|
22718
|
-
REFERENCES daemon_slots(agent_name, provider, model, slot_key)
|
|
22719
|
-
ON DELETE CASCADE
|
|
22720
|
-
);
|
|
22721
|
-
|
|
22722
|
-
CREATE TABLE IF NOT EXISTS daemon_slot_workspaces (
|
|
22723
|
-
agent_name TEXT NOT NULL,
|
|
22724
|
-
provider TEXT NOT NULL,
|
|
22725
|
-
model TEXT NOT NULL,
|
|
22726
|
-
slot_key TEXT NOT NULL,
|
|
22727
|
-
workspace_id TEXT NOT NULL UNIQUE,
|
|
22728
|
-
worktree_path TEXT NOT NULL,
|
|
22729
|
-
worktree_branch TEXT,
|
|
22730
|
-
PRIMARY KEY (agent_name, provider, model, slot_key),
|
|
22731
|
-
FOREIGN KEY (agent_name, provider, model, slot_key)
|
|
22732
|
-
REFERENCES daemon_slots(agent_name, provider, model, slot_key)
|
|
22733
|
-
ON DELETE CASCADE
|
|
22734
|
-
);
|
|
22735
|
-
|
|
22736
|
-
CREATE INDEX IF NOT EXISTS daemon_slots_expires_idx
|
|
22737
|
-
ON daemon_slots (expires_at_ms);
|
|
22738
|
-
|
|
22739
|
-
CREATE INDEX IF NOT EXISTS daemon_slots_task_attempt_idx
|
|
22740
|
-
ON daemon_slots (
|
|
22741
|
-
last_task_id,
|
|
22742
|
-
last_attempt_n,
|
|
22743
|
-
last_used_at_ms DESC
|
|
22744
|
-
);
|
|
22745
|
-
|
|
22746
|
-
ALTER TABLE daemon_slots
|
|
22747
|
-
ALTER COLUMN created_at_ms TYPE BIGINT,
|
|
22748
|
-
ALTER COLUMN last_used_at_ms TYPE BIGINT,
|
|
22749
|
-
ALTER COLUMN expires_at_ms TYPE BIGINT;
|
|
22750
|
-
`));
|
|
22942
|
+
await this.withDb("initialize postgres schema", () => runPgMigrations(this.pool));
|
|
22751
22943
|
}
|
|
22752
22944
|
async lookupSession(slot) {
|
|
22753
22945
|
const [session] = await this.withDb("select postgres slot session", () => this.db.select().from(pgDaemonSlotSessions).where(pgSlotSessionIdentityWhere(slot, slot.slotKey)).limit(1).execute());
|
|
22754
22946
|
return session ?? null;
|
|
22755
22947
|
}
|
|
22756
22948
|
async lookupWorkspace(slot) {
|
|
22757
|
-
|
|
22949
|
+
if (!slot.workspaceId) return null;
|
|
22950
|
+
const [workspace] = await this.withDb("select postgres workspace", () => this.db.select().from(pgDaemonWorkspaces).where(eq(pgDaemonWorkspaces.workspaceId, slot.workspaceId)).limit(1).execute());
|
|
22758
22951
|
return workspace ?? null;
|
|
22759
22952
|
}
|
|
22760
22953
|
async withDb(operation, fn) {
|
|
@@ -22794,24 +22987,27 @@ function sqliteSlotIdentityWhere(identity, slotKey) {
|
|
|
22794
22987
|
function sqliteSlotSessionIdentityWhere(identity, slotKey) {
|
|
22795
22988
|
return and(eq(daemonSlotSessions.agentName, identity.agentName), eq(daemonSlotSessions.provider, identity.provider), eq(daemonSlotSessions.model, identity.model), eq(daemonSlotSessions.slotKey, slotKey));
|
|
22796
22989
|
}
|
|
22797
|
-
function sqliteSlotWorkspaceIdentityWhere(identity, slotKey) {
|
|
22798
|
-
return and(eq(daemonSlotWorkspaces.agentName, identity.agentName), eq(daemonSlotWorkspaces.provider, identity.provider), eq(daemonSlotWorkspaces.model, identity.model), eq(daemonSlotWorkspaces.slotKey, slotKey));
|
|
22799
|
-
}
|
|
22800
22990
|
function pgSlotIdentityWhere(identity, slotKey) {
|
|
22801
22991
|
return and(eq(pgDaemonSlots.agentName, identity.agentName), eq(pgDaemonSlots.provider, identity.provider), eq(pgDaemonSlots.model, identity.model), eq(pgDaemonSlots.slotKey, slotKey));
|
|
22802
22992
|
}
|
|
22803
22993
|
function pgSlotSessionIdentityWhere(identity, slotKey) {
|
|
22804
22994
|
return and(eq(pgDaemonSlotSessions.agentName, identity.agentName), eq(pgDaemonSlotSessions.provider, identity.provider), eq(pgDaemonSlotSessions.model, identity.model), eq(pgDaemonSlotSessions.slotKey, slotKey));
|
|
22805
22995
|
}
|
|
22806
|
-
|
|
22807
|
-
|
|
22996
|
+
/** Remove the persisted Pi session dir for each reaped slot. */
|
|
22997
|
+
function cleanupReapedSessions(slots) {
|
|
22998
|
+
for (const item of slots) if (item.session) cleanupPiSessionDir(item.session.sessionDir);
|
|
22808
22999
|
}
|
|
22809
|
-
|
|
22810
|
-
|
|
22811
|
-
|
|
22812
|
-
|
|
22813
|
-
|
|
22814
|
-
|
|
23000
|
+
/**
|
|
23001
|
+
* Remove on-disk artifacts for workspaces whose refcount reached 0. Worktrees
|
|
23002
|
+
* (origin/fork) are removed via `git worktree remove`; scratch workspaces are
|
|
23003
|
+
* plain directories removed via rm.
|
|
23004
|
+
*/
|
|
23005
|
+
function cleanupReleasableWorkspaces(workspaces) {
|
|
23006
|
+
for (const ws of workspaces) if (ws.kind === "scratch") rmSync(ws.worktreePath, {
|
|
23007
|
+
recursive: true,
|
|
23008
|
+
force: true
|
|
23009
|
+
});
|
|
23010
|
+
else cleanupReusableWorktree(ws.worktreePath);
|
|
22815
23011
|
}
|
|
22816
23012
|
function cleanupPiSessionDir(sessionDir) {
|
|
22817
23013
|
rmSync(sessionDir, {
|
|
@@ -24323,7 +24519,7 @@ function buildRunEvalUserPrompt(input, ctx) {
|
|
|
24323
24519
|
function buildTaskUserPrompt(task, ctx) {
|
|
24324
24520
|
switch (task.taskType) {
|
|
24325
24521
|
case FREEFORM_TYPE:
|
|
24326
|
-
if (!Check(FreeformInput, task.input)) {
|
|
24522
|
+
if (!Check$2(FreeformInput, task.input)) {
|
|
24327
24523
|
const errors = [...Errors(FreeformInput, task.input)];
|
|
24328
24524
|
throw new Error(`freeform input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
24329
24525
|
}
|
|
@@ -24332,7 +24528,7 @@ function buildTaskUserPrompt(task, ctx) {
|
|
|
24332
24528
|
priorContext: ctx.priorContext
|
|
24333
24529
|
});
|
|
24334
24530
|
case FULFILL_BRIEF_TYPE:
|
|
24335
|
-
if (!Check(FulfillBriefInput, task.input)) {
|
|
24531
|
+
if (!Check$2(FulfillBriefInput, task.input)) {
|
|
24336
24532
|
const errors = [...Errors(FulfillBriefInput, task.input)];
|
|
24337
24533
|
throw new Error(`fulfill_brief input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
24338
24534
|
}
|
|
@@ -24343,7 +24539,7 @@ function buildTaskUserPrompt(task, ctx) {
|
|
|
24343
24539
|
workspace: ctx.workspace
|
|
24344
24540
|
});
|
|
24345
24541
|
case ASSESS_BRIEF_TYPE:
|
|
24346
|
-
if (!Check(AssessBriefInput, task.input)) {
|
|
24542
|
+
if (!Check$2(AssessBriefInput, task.input)) {
|
|
24347
24543
|
const errors = [...Errors(AssessBriefInput, task.input)];
|
|
24348
24544
|
throw new Error(`assess_brief input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
24349
24545
|
}
|
|
@@ -24353,7 +24549,7 @@ function buildTaskUserPrompt(task, ctx) {
|
|
|
24353
24549
|
workspace: ctx.workspace
|
|
24354
24550
|
});
|
|
24355
24551
|
case CURATE_PACK_TYPE:
|
|
24356
|
-
if (!Check(CuratePackInput, task.input)) {
|
|
24552
|
+
if (!Check$2(CuratePackInput, task.input)) {
|
|
24357
24553
|
const errors = [...Errors(CuratePackInput, task.input)];
|
|
24358
24554
|
throw new Error(`curate_pack input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
24359
24555
|
}
|
|
@@ -24362,7 +24558,7 @@ function buildTaskUserPrompt(task, ctx) {
|
|
|
24362
24558
|
taskId: ctx.taskId
|
|
24363
24559
|
});
|
|
24364
24560
|
case RENDER_PACK_TYPE:
|
|
24365
|
-
if (!Check(RenderPackInput, task.input)) {
|
|
24561
|
+
if (!Check$2(RenderPackInput, task.input)) {
|
|
24366
24562
|
const errors = [...Errors(RenderPackInput, task.input)];
|
|
24367
24563
|
throw new Error(`render_pack input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
24368
24564
|
}
|
|
@@ -24371,7 +24567,7 @@ function buildTaskUserPrompt(task, ctx) {
|
|
|
24371
24567
|
taskId: ctx.taskId
|
|
24372
24568
|
});
|
|
24373
24569
|
case JUDGE_PACK_TYPE:
|
|
24374
|
-
if (!Check(JudgePackInput, task.input)) {
|
|
24570
|
+
if (!Check$2(JudgePackInput, task.input)) {
|
|
24375
24571
|
const errors = [...Errors(JudgePackInput, task.input)];
|
|
24376
24572
|
throw new Error(`judge_pack input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
24377
24573
|
}
|
|
@@ -24380,7 +24576,7 @@ function buildTaskUserPrompt(task, ctx) {
|
|
|
24380
24576
|
taskId: ctx.taskId
|
|
24381
24577
|
});
|
|
24382
24578
|
case JUDGE_EVAL_ATTEMPT_TYPE:
|
|
24383
|
-
if (!Check(JudgeEvalAttemptInput, task.input)) {
|
|
24579
|
+
if (!Check$2(JudgeEvalAttemptInput, task.input)) {
|
|
24384
24580
|
const errors = [...Errors(JudgeEvalAttemptInput, task.input)];
|
|
24385
24581
|
throw new Error(`judge_eval_attempt input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
24386
24582
|
}
|
|
@@ -24390,7 +24586,7 @@ function buildTaskUserPrompt(task, ctx) {
|
|
|
24390
24586
|
workspace: ctx.workspace
|
|
24391
24587
|
});
|
|
24392
24588
|
case PR_REVIEW_TYPE:
|
|
24393
|
-
if (!Check(PrReviewInput, task.input)) {
|
|
24589
|
+
if (!Check$2(PrReviewInput, task.input)) {
|
|
24394
24590
|
const errors = [...Errors(PrReviewInput, task.input)];
|
|
24395
24591
|
throw new Error(`pr_review input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
24396
24592
|
}
|
|
@@ -24400,7 +24596,7 @@ function buildTaskUserPrompt(task, ctx) {
|
|
|
24400
24596
|
workspace: ctx.workspace
|
|
24401
24597
|
});
|
|
24402
24598
|
case RUN_EVAL_TYPE:
|
|
24403
|
-
if (!Check(RunEvalInput, task.input)) {
|
|
24599
|
+
if (!Check$2(RunEvalInput, task.input)) {
|
|
24404
24600
|
const errors = [...Errors(RunEvalInput, task.input)];
|
|
24405
24601
|
throw new Error(`run_eval input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
24406
24602
|
}
|
|
@@ -32845,8 +33041,7 @@ async function resumeVm(config) {
|
|
|
32845
33041
|
signal: config.signal
|
|
32846
33042
|
});
|
|
32847
33043
|
if (creds.gitconfig) {
|
|
32848
|
-
const
|
|
32849
|
-
const vmGitconfig = creds.gitconfig.replace(/signingKey\s*=\s*.+/g, `signingKey = ${vmSigningKey}`);
|
|
33044
|
+
const vmGitconfig = rewriteGitconfigPaths(creds.gitconfig, vmSshDir, vmAgentDir);
|
|
32850
33045
|
await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, {
|
|
32851
33046
|
mode: 420,
|
|
32852
33047
|
signal: config.signal
|
|
@@ -32869,17 +33064,6 @@ async function resumeVm(config) {
|
|
|
32869
33064
|
signal: config.signal
|
|
32870
33065
|
});
|
|
32871
33066
|
await vm.exec("chown -R agent:agent /home/agent/.pi /home/agent/.moltnet", { signal: config.signal });
|
|
32872
|
-
const gitCredHelperPath = `${vmSshDir}/git-credential-moltnet`;
|
|
32873
|
-
const credHelperScript = `#!/bin/sh
|
|
32874
|
-
echo "username=x-access-token"
|
|
32875
|
-
echo "password=$(moltnet github token --credentials ${vmSshDir}/moltnet.json)"
|
|
32876
|
-
`;
|
|
32877
|
-
await vm.fs.writeFile(gitCredHelperPath, credHelperScript, {
|
|
32878
|
-
mode: 493,
|
|
32879
|
-
signal: config.signal
|
|
32880
|
-
});
|
|
32881
|
-
await vmRun(vm, "git credential helper", `git config --global credential.helper ${gitCredHelperPath} && \
|
|
32882
|
-
git config --global url."https://github.com/".insteadOf "git@github.com:"`, config.signal);
|
|
32883
33067
|
return {
|
|
32884
33068
|
vm,
|
|
32885
33069
|
credentials: creds,
|
|
@@ -32898,6 +33082,30 @@ echo "password=$(moltnet github token --credentials ${vmSshDir}/moltnet.json)"
|
|
|
32898
33082
|
}
|
|
32899
33083
|
}
|
|
32900
33084
|
/**
|
|
33085
|
+
* Rewrite host-absolute paths inside an agent gitconfig to VM-local
|
|
33086
|
+
* equivalents before injecting it into the guest.
|
|
33087
|
+
*
|
|
33088
|
+
* Two rewrites:
|
|
33089
|
+
* - `signingKey = <host path>` → `<vmSshDir>/id_ed25519`
|
|
33090
|
+
* - `... credential-helper --credentials <host moltnet.json>`
|
|
33091
|
+
* → `<vmAgentDir>/moltnet.json`
|
|
33092
|
+
*
|
|
33093
|
+
* The credential-helper line is generated host-side by `moltnet github setup`
|
|
33094
|
+
* with a host-absolute `--credentials` path; inside the guest that path is
|
|
33095
|
+
* invalid, so it must point at the VM-side moltnet.json. The `insteadOf`
|
|
33096
|
+
* rewrite rule and every other line are workspace-independent and pass through
|
|
33097
|
+
* unchanged. A gitconfig without a credential helper is rewritten only for
|
|
33098
|
+
* `signingKey`.
|
|
33099
|
+
*
|
|
33100
|
+
* This is the single source of truth for git push auth in the guest: the
|
|
33101
|
+
* injected gitconfig carries the tokenless mint-on-demand helper, so the VM
|
|
33102
|
+
* no longer hand-rolls a credential-helper script or runs an imperative
|
|
33103
|
+
* `git config --global ... insteadOf` against the guest $HOME.
|
|
33104
|
+
*/
|
|
33105
|
+
function rewriteGitconfigPaths(gitconfig, vmSshDir, vmAgentDir) {
|
|
33106
|
+
return gitconfig.replace(/signingKey\s*=\s*.+/g, `signingKey = ${vmSshDir}/id_ed25519`).replace(/(moltnet github credential-helper --credentials )\S+/g, `$1${vmAgentDir}/moltnet.json`);
|
|
33107
|
+
}
|
|
33108
|
+
/**
|
|
32901
33109
|
* Rewrite host-absolute paths inside moltnet.json to VM-local equivalents.
|
|
32902
33110
|
*
|
|
32903
33111
|
* Fields rewritten:
|
|
@@ -33458,7 +33666,7 @@ function createSubagentTool(args) {
|
|
|
33458
33666
|
description: subagentToolDescription(),
|
|
33459
33667
|
parameters: SubagentToolParameters,
|
|
33460
33668
|
async execute(_id, params) {
|
|
33461
|
-
if (!Check(SubagentToolParameters, params)) return toolError(`subagent: invalid parameters: ${JSON.stringify([...Errors(SubagentToolParameters, params)].slice(0, 3))}`);
|
|
33669
|
+
if (!Check$2(SubagentToolParameters, params)) return toolError(`subagent: invalid parameters: ${JSON.stringify([...Errors(SubagentToolParameters, params)].slice(0, 3))}`);
|
|
33462
33670
|
const { task, output_schema } = params;
|
|
33463
33671
|
const contract = contractRegistry.get(output_schema);
|
|
33464
33672
|
if (!contract) return toolError(`subagent: unknown output_schema "${output_schema}". Registered contracts: [${contractRegistry.list().map((c) => c.name).join(", ")}]`);
|
|
@@ -33471,7 +33679,7 @@ function createSubagentTool(args) {
|
|
|
33471
33679
|
description: `Submit your structured output for this subagent task. Call exactly once when done. Args MUST match the ${output_schema} contract; mismatches return a tool error you can recover from in the same session.`,
|
|
33472
33680
|
parameters: contract.parametersSchema,
|
|
33473
33681
|
async execute(_innerId, innerParams) {
|
|
33474
|
-
if (!Check(contract.parametersSchema, innerParams)) return toolError(`submit_subagent_output: schema validation failed: ${[...Errors(contract.parametersSchema, innerParams)].slice(0, 3).map((e) => `${e.instancePath}: ${e.message}`).join("; ")}. Re-call with a corrected payload.`);
|
|
33682
|
+
if (!Check$2(contract.parametersSchema, innerParams)) return toolError(`submit_subagent_output: schema validation failed: ${[...Errors(contract.parametersSchema, innerParams)].slice(0, 3).map((e) => `${e.instancePath}: ${e.message}`).join("; ")}. Re-call with a corrected payload.`);
|
|
33475
33683
|
captured = innerParams;
|
|
33476
33684
|
return {
|
|
33477
33685
|
content: [{
|
|
@@ -33877,10 +34085,11 @@ function prepareTaskWorkspace(task, requestedMountPath, executionPlan) {
|
|
|
33877
34085
|
const relMount = relative(mainRepo, requestedMountPath);
|
|
33878
34086
|
const cwdPath = relMount === "" || relMount.startsWith("..") ? worktreeDir : join(worktreeDir, relMount);
|
|
33879
34087
|
const keepWorkspace = executionPlan?.workspaceScope === "session" && executionPlan.sessionKey !== null;
|
|
33880
|
-
|
|
34088
|
+
const baseRefOverride = executionPlan?.worktreeBaseRef ?? null;
|
|
34089
|
+
if (keepWorkspace) ensureReusableTaskWorktree(mainRepo, worktreeDir, branch, baseRefOverride);
|
|
33881
34090
|
else {
|
|
33882
34091
|
removeExistingTaskWorktree(mainRepo, worktreeDir);
|
|
33883
|
-
addTaskWorktree(mainRepo, worktreeDir, branch);
|
|
34092
|
+
addTaskWorktree(mainRepo, worktreeDir, branch, baseRefOverride);
|
|
33884
34093
|
}
|
|
33885
34094
|
return {
|
|
33886
34095
|
mountPath: mainRepo,
|
|
@@ -33905,14 +34114,15 @@ function resolveTaskWorktreePath(mainRepo, workspaceId) {
|
|
|
33905
34114
|
function resolveTaskScratchPath(mainRepo, workspaceId) {
|
|
33906
34115
|
return join(mainRepo, ".moltnet", "d", "task-workspaces", workspaceId);
|
|
33907
34116
|
}
|
|
33908
|
-
function ensureReusableTaskWorktree(mainRepo, worktreeDir, branch) {
|
|
34117
|
+
function ensureReusableTaskWorktree(mainRepo, worktreeDir, branch, baseRefOverride = null) {
|
|
33909
34118
|
if (isRegisteredWorktree(mainRepo, worktreeDir)) return;
|
|
33910
34119
|
if (existsSync(worktreeDir)) throw new Error(`Expected reusable worktree ${worktreeDir} to be git-managed, but it exists outside git worktree metadata.`);
|
|
33911
|
-
addTaskWorktree(mainRepo, worktreeDir, branch);
|
|
34120
|
+
addTaskWorktree(mainRepo, worktreeDir, branch, baseRefOverride);
|
|
33912
34121
|
}
|
|
33913
|
-
function addTaskWorktree(mainRepo, worktreeDir, branch) {
|
|
33914
|
-
const
|
|
33915
|
-
|
|
34122
|
+
function addTaskWorktree(mainRepo, worktreeDir, branch, baseRefOverride = null) {
|
|
34123
|
+
const branchExists = gitRefExists(mainRepo, `refs/heads/${branch}`);
|
|
34124
|
+
const baseRef = baseRefOverride ?? resolveWorktreeBaseRef(mainRepo);
|
|
34125
|
+
execFileSync("git", branchExists ? [
|
|
33916
34126
|
"-C",
|
|
33917
34127
|
mainRepo,
|
|
33918
34128
|
"worktree",
|
|
@@ -34295,7 +34505,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34295
34505
|
let injectedContext;
|
|
34296
34506
|
try {
|
|
34297
34507
|
const contextArray = rawContext === void 0 ? [] : rawContext;
|
|
34298
|
-
if (!Check(TaskContext, contextArray)) throw new Error(`task.input.context failed TaskContext validation: ${JSON.stringify([...Errors(TaskContext, contextArray)].slice(0, 3))}`);
|
|
34508
|
+
if (!Check$2(TaskContext, contextArray)) throw new Error(`task.input.context failed TaskContext validation: ${JSON.stringify([...Errors(TaskContext, contextArray)].slice(0, 3))}`);
|
|
34299
34509
|
injectedContext = await injectTaskContext({
|
|
34300
34510
|
context: contextArray,
|
|
34301
34511
|
fs: managed.vm.fs,
|
|
@@ -35074,13 +35284,32 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
35074
35284
|
const resolution = await resolveWarmSlot(slotRegistry, continueFrom.taskId, continueFrom.attemptN, stateDirs);
|
|
35075
35285
|
if (resolution.kind === "missing") throw new ProducerContextResolutionError(`Continuation source task ${continueFrom.taskId} attempt ${continueFrom.attemptN} has no live daemon slot on this daemon — claim affinity filter should have prevented this claim`);
|
|
35076
35286
|
if (resolution.kind === "no-session-path") throw new ProducerContextResolutionError(`Continuation source attempt ${continueFrom.taskId}/${continueFrom.attemptN} has no persisted Pi session path`);
|
|
35287
|
+
const sessionDir = `${stateDirs.piSessionsDir}/continue-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
|
|
35288
|
+
const parentBranch = resolution.producerSlot.workspace?.worktreeBranch ?? null;
|
|
35289
|
+
if (continueFrom.mode === "fork") {
|
|
35290
|
+
if (!parentBranch) throw new ProducerContextResolutionError(`Cannot fork continuation of ${continueFrom.taskId}/${continueFrom.attemptN}: producer slot has no worktree branch to fork from`);
|
|
35291
|
+
const forkWorkspaceId = `fork-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
|
|
35292
|
+
const forkBranch = `${parentBranch}-fork-${claimedTask.task.id.slice(0, 8)}-${claimedTask.attemptN}`;
|
|
35293
|
+
return {
|
|
35294
|
+
...basePlan,
|
|
35295
|
+
workspaceMode: "dedicated_worktree",
|
|
35296
|
+
workspaceId: forkWorkspaceId,
|
|
35297
|
+
worktreeBranch: forkBranch,
|
|
35298
|
+
worktreeBaseRef: parentBranch,
|
|
35299
|
+
workspaceKind: "fork",
|
|
35300
|
+
sessionPersistence: {
|
|
35301
|
+
sessionDir,
|
|
35302
|
+
forkFromSessionPath: resolution.sessionPath
|
|
35303
|
+
}
|
|
35304
|
+
};
|
|
35305
|
+
}
|
|
35077
35306
|
return {
|
|
35078
35307
|
...basePlan,
|
|
35079
35308
|
workspaceMode: "dedicated_worktree",
|
|
35080
35309
|
workspaceId: resolution.producerSlot.workspace?.workspaceId ?? null,
|
|
35081
|
-
worktreeBranch:
|
|
35310
|
+
worktreeBranch: parentBranch,
|
|
35082
35311
|
sessionPersistence: {
|
|
35083
|
-
sessionDir
|
|
35312
|
+
sessionDir,
|
|
35084
35313
|
forkFromSessionPath: resolution.sessionPath
|
|
35085
35314
|
}
|
|
35086
35315
|
};
|
|
@@ -35096,6 +35325,7 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
35096
35325
|
...basePlan,
|
|
35097
35326
|
workspaceMode: "scratch_mount",
|
|
35098
35327
|
worktreeBranch: null,
|
|
35328
|
+
workspaceKind: "scratch",
|
|
35099
35329
|
workspaceSeed: {
|
|
35100
35330
|
copyFromPath: resolution.workspacePath,
|
|
35101
35331
|
source: "producer"
|
|
@@ -35900,6 +36130,7 @@ async function runPolling(opts) {
|
|
|
35900
36130
|
workspaceId: executionPlan.workspaceId,
|
|
35901
36131
|
worktreePath: resolveRecordedWorkspacePath$1(mainRepo, stateDirs.rootDir, executionPlan),
|
|
35902
36132
|
worktreeBranch: executionPlan.worktreeBranch,
|
|
36133
|
+
workspaceKind: executionPlan.workspaceKind,
|
|
35903
36134
|
lastTaskId: claimedTask.task.id,
|
|
35904
36135
|
lastAttemptN: claimedTask.attemptN,
|
|
35905
36136
|
ttlSec: common.warmSessionTtlSec
|
|
@@ -36158,6 +36389,7 @@ async function runOnce(argv) {
|
|
|
36158
36389
|
workspaceId: executionPlan.workspaceId,
|
|
36159
36390
|
worktreePath: resolveRecordedWorkspacePath(mainRepo, stateDirs.rootDir, executionPlan),
|
|
36160
36391
|
worktreeBranch: executionPlan.worktreeBranch,
|
|
36392
|
+
workspaceKind: executionPlan.workspaceKind,
|
|
36161
36393
|
lastTaskId: claimedTask.task.id,
|
|
36162
36394
|
lastAttemptN: claimedTask.attemptN,
|
|
36163
36395
|
ttlSec: opts.warmSessionTtlSec
|