@themoltnet/pi-extension 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -41
- package/dist/index.d.ts +185 -7
- package/dist/index.js +1378 -716
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync } fr
|
|
|
4
4
|
import path, { join } from "node:path";
|
|
5
5
|
import { DefaultResourceLoader, SessionManager, createAgentSession, createBashTool, createBashToolDefinition, createEditTool, createEditToolDefinition, createReadTool, createReadToolDefinition, createWriteTool, createWriteToolDefinition, defineTool } from "@mariozechner/pi-coding-agent";
|
|
6
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
7
|
-
import { createHash as createHash$1 } from "crypto";
|
|
7
|
+
import crypto, { createHash as createHash$1 } from "crypto";
|
|
8
8
|
import { readFile } from "node:fs/promises";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { Type, complete, getModel } from "@mariozechner/pi-ai";
|
|
@@ -13,7 +13,6 @@ import { RealFSProvider, ShadowProvider, VM, VmCheckpoint, createHttpHooks, crea
|
|
|
13
13
|
import { parseEnv } from "node:util";
|
|
14
14
|
import { FormatRegistry, Type as Type$1 } from "@sinclair/typebox";
|
|
15
15
|
import { Value } from "@sinclair/typebox/value";
|
|
16
|
-
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
|
17
16
|
//#region ../api-client/src/generated/core/bodySerializer.gen.ts
|
|
18
17
|
var jsonBodySerializer = { bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value) };
|
|
19
18
|
Object.entries({
|
|
@@ -2039,6 +2038,276 @@ var getLegreffierOnboardingStatus = (options) => (options.client ?? client).get(
|
|
|
2039
2038
|
...options
|
|
2040
2039
|
});
|
|
2041
2040
|
/**
|
|
2041
|
+
* List tasks for a team with optional filters.
|
|
2042
|
+
*/
|
|
2043
|
+
var listTasks = (options) => (options.client ?? client).get({
|
|
2044
|
+
security: [
|
|
2045
|
+
{
|
|
2046
|
+
scheme: "bearer",
|
|
2047
|
+
type: "http"
|
|
2048
|
+
},
|
|
2049
|
+
{
|
|
2050
|
+
name: "X-Moltnet-Session-Token",
|
|
2051
|
+
type: "apiKey"
|
|
2052
|
+
},
|
|
2053
|
+
{
|
|
2054
|
+
in: "cookie",
|
|
2055
|
+
name: "ory_kratos_session",
|
|
2056
|
+
type: "apiKey"
|
|
2057
|
+
}
|
|
2058
|
+
],
|
|
2059
|
+
url: "/tasks",
|
|
2060
|
+
...options
|
|
2061
|
+
});
|
|
2062
|
+
/**
|
|
2063
|
+
* Create and enqueue a new task.
|
|
2064
|
+
*/
|
|
2065
|
+
var createTask = (options) => (options.client ?? client).post({
|
|
2066
|
+
security: [
|
|
2067
|
+
{
|
|
2068
|
+
scheme: "bearer",
|
|
2069
|
+
type: "http"
|
|
2070
|
+
},
|
|
2071
|
+
{
|
|
2072
|
+
name: "X-Moltnet-Session-Token",
|
|
2073
|
+
type: "apiKey"
|
|
2074
|
+
},
|
|
2075
|
+
{
|
|
2076
|
+
in: "cookie",
|
|
2077
|
+
name: "ory_kratos_session",
|
|
2078
|
+
type: "apiKey"
|
|
2079
|
+
}
|
|
2080
|
+
],
|
|
2081
|
+
url: "/tasks",
|
|
2082
|
+
...options,
|
|
2083
|
+
headers: {
|
|
2084
|
+
"Content-Type": "application/json",
|
|
2085
|
+
...options.headers
|
|
2086
|
+
}
|
|
2087
|
+
});
|
|
2088
|
+
/**
|
|
2089
|
+
* Get a task by ID.
|
|
2090
|
+
*/
|
|
2091
|
+
var getTask = (options) => (options.client ?? client).get({
|
|
2092
|
+
security: [
|
|
2093
|
+
{
|
|
2094
|
+
scheme: "bearer",
|
|
2095
|
+
type: "http"
|
|
2096
|
+
},
|
|
2097
|
+
{
|
|
2098
|
+
name: "X-Moltnet-Session-Token",
|
|
2099
|
+
type: "apiKey"
|
|
2100
|
+
},
|
|
2101
|
+
{
|
|
2102
|
+
in: "cookie",
|
|
2103
|
+
name: "ory_kratos_session",
|
|
2104
|
+
type: "apiKey"
|
|
2105
|
+
}
|
|
2106
|
+
],
|
|
2107
|
+
url: "/tasks/{id}",
|
|
2108
|
+
...options
|
|
2109
|
+
});
|
|
2110
|
+
/**
|
|
2111
|
+
* Claim a queued task and start an attempt.
|
|
2112
|
+
*/
|
|
2113
|
+
var claimTask = (options) => (options.client ?? client).post({
|
|
2114
|
+
security: [
|
|
2115
|
+
{
|
|
2116
|
+
scheme: "bearer",
|
|
2117
|
+
type: "http"
|
|
2118
|
+
},
|
|
2119
|
+
{
|
|
2120
|
+
name: "X-Moltnet-Session-Token",
|
|
2121
|
+
type: "apiKey"
|
|
2122
|
+
},
|
|
2123
|
+
{
|
|
2124
|
+
in: "cookie",
|
|
2125
|
+
name: "ory_kratos_session",
|
|
2126
|
+
type: "apiKey"
|
|
2127
|
+
}
|
|
2128
|
+
],
|
|
2129
|
+
url: "/tasks/{id}/claim",
|
|
2130
|
+
...options,
|
|
2131
|
+
headers: {
|
|
2132
|
+
"Content-Type": "application/json",
|
|
2133
|
+
...options.headers
|
|
2134
|
+
}
|
|
2135
|
+
});
|
|
2136
|
+
/**
|
|
2137
|
+
* Send a heartbeat to keep the attempt lease alive.
|
|
2138
|
+
*/
|
|
2139
|
+
var taskHeartbeat = (options) => (options.client ?? client).post({
|
|
2140
|
+
security: [
|
|
2141
|
+
{
|
|
2142
|
+
scheme: "bearer",
|
|
2143
|
+
type: "http"
|
|
2144
|
+
},
|
|
2145
|
+
{
|
|
2146
|
+
name: "X-Moltnet-Session-Token",
|
|
2147
|
+
type: "apiKey"
|
|
2148
|
+
},
|
|
2149
|
+
{
|
|
2150
|
+
in: "cookie",
|
|
2151
|
+
name: "ory_kratos_session",
|
|
2152
|
+
type: "apiKey"
|
|
2153
|
+
}
|
|
2154
|
+
],
|
|
2155
|
+
url: "/tasks/{id}/attempts/{n}/heartbeat",
|
|
2156
|
+
...options,
|
|
2157
|
+
headers: {
|
|
2158
|
+
"Content-Type": "application/json",
|
|
2159
|
+
...options.headers
|
|
2160
|
+
}
|
|
2161
|
+
});
|
|
2162
|
+
/**
|
|
2163
|
+
* Mark an attempt as completed with output.
|
|
2164
|
+
*/
|
|
2165
|
+
var completeTask = (options) => (options.client ?? client).post({
|
|
2166
|
+
security: [
|
|
2167
|
+
{
|
|
2168
|
+
scheme: "bearer",
|
|
2169
|
+
type: "http"
|
|
2170
|
+
},
|
|
2171
|
+
{
|
|
2172
|
+
name: "X-Moltnet-Session-Token",
|
|
2173
|
+
type: "apiKey"
|
|
2174
|
+
},
|
|
2175
|
+
{
|
|
2176
|
+
in: "cookie",
|
|
2177
|
+
name: "ory_kratos_session",
|
|
2178
|
+
type: "apiKey"
|
|
2179
|
+
}
|
|
2180
|
+
],
|
|
2181
|
+
url: "/tasks/{id}/attempts/{n}/complete",
|
|
2182
|
+
...options,
|
|
2183
|
+
headers: {
|
|
2184
|
+
"Content-Type": "application/json",
|
|
2185
|
+
...options.headers
|
|
2186
|
+
}
|
|
2187
|
+
});
|
|
2188
|
+
/**
|
|
2189
|
+
* Mark an attempt as failed with error details.
|
|
2190
|
+
*/
|
|
2191
|
+
var failTask = (options) => (options.client ?? client).post({
|
|
2192
|
+
security: [
|
|
2193
|
+
{
|
|
2194
|
+
scheme: "bearer",
|
|
2195
|
+
type: "http"
|
|
2196
|
+
},
|
|
2197
|
+
{
|
|
2198
|
+
name: "X-Moltnet-Session-Token",
|
|
2199
|
+
type: "apiKey"
|
|
2200
|
+
},
|
|
2201
|
+
{
|
|
2202
|
+
in: "cookie",
|
|
2203
|
+
name: "ory_kratos_session",
|
|
2204
|
+
type: "apiKey"
|
|
2205
|
+
}
|
|
2206
|
+
],
|
|
2207
|
+
url: "/tasks/{id}/attempts/{n}/fail",
|
|
2208
|
+
...options,
|
|
2209
|
+
headers: {
|
|
2210
|
+
"Content-Type": "application/json",
|
|
2211
|
+
...options.headers
|
|
2212
|
+
}
|
|
2213
|
+
});
|
|
2214
|
+
/**
|
|
2215
|
+
* Cancel a task.
|
|
2216
|
+
*/
|
|
2217
|
+
var cancelTask = (options) => (options.client ?? client).post({
|
|
2218
|
+
security: [
|
|
2219
|
+
{
|
|
2220
|
+
scheme: "bearer",
|
|
2221
|
+
type: "http"
|
|
2222
|
+
},
|
|
2223
|
+
{
|
|
2224
|
+
name: "X-Moltnet-Session-Token",
|
|
2225
|
+
type: "apiKey"
|
|
2226
|
+
},
|
|
2227
|
+
{
|
|
2228
|
+
in: "cookie",
|
|
2229
|
+
name: "ory_kratos_session",
|
|
2230
|
+
type: "apiKey"
|
|
2231
|
+
}
|
|
2232
|
+
],
|
|
2233
|
+
url: "/tasks/{id}/cancel",
|
|
2234
|
+
...options,
|
|
2235
|
+
headers: {
|
|
2236
|
+
"Content-Type": "application/json",
|
|
2237
|
+
...options.headers
|
|
2238
|
+
}
|
|
2239
|
+
});
|
|
2240
|
+
/**
|
|
2241
|
+
* List all attempts for a task.
|
|
2242
|
+
*/
|
|
2243
|
+
var listTaskAttempts = (options) => (options.client ?? client).get({
|
|
2244
|
+
security: [
|
|
2245
|
+
{
|
|
2246
|
+
scheme: "bearer",
|
|
2247
|
+
type: "http"
|
|
2248
|
+
},
|
|
2249
|
+
{
|
|
2250
|
+
name: "X-Moltnet-Session-Token",
|
|
2251
|
+
type: "apiKey"
|
|
2252
|
+
},
|
|
2253
|
+
{
|
|
2254
|
+
in: "cookie",
|
|
2255
|
+
name: "ory_kratos_session",
|
|
2256
|
+
type: "apiKey"
|
|
2257
|
+
}
|
|
2258
|
+
],
|
|
2259
|
+
url: "/tasks/{id}/attempts",
|
|
2260
|
+
...options
|
|
2261
|
+
});
|
|
2262
|
+
/**
|
|
2263
|
+
* List messages for a task attempt.
|
|
2264
|
+
*/
|
|
2265
|
+
var listTaskMessages = (options) => (options.client ?? client).get({
|
|
2266
|
+
security: [
|
|
2267
|
+
{
|
|
2268
|
+
scheme: "bearer",
|
|
2269
|
+
type: "http"
|
|
2270
|
+
},
|
|
2271
|
+
{
|
|
2272
|
+
name: "X-Moltnet-Session-Token",
|
|
2273
|
+
type: "apiKey"
|
|
2274
|
+
},
|
|
2275
|
+
{
|
|
2276
|
+
in: "cookie",
|
|
2277
|
+
name: "ory_kratos_session",
|
|
2278
|
+
type: "apiKey"
|
|
2279
|
+
}
|
|
2280
|
+
],
|
|
2281
|
+
url: "/tasks/{id}/attempts/{n}/messages",
|
|
2282
|
+
...options
|
|
2283
|
+
});
|
|
2284
|
+
/**
|
|
2285
|
+
* Append messages to a task attempt.
|
|
2286
|
+
*/
|
|
2287
|
+
var appendTaskMessages = (options) => (options.client ?? client).post({
|
|
2288
|
+
security: [
|
|
2289
|
+
{
|
|
2290
|
+
scheme: "bearer",
|
|
2291
|
+
type: "http"
|
|
2292
|
+
},
|
|
2293
|
+
{
|
|
2294
|
+
name: "X-Moltnet-Session-Token",
|
|
2295
|
+
type: "apiKey"
|
|
2296
|
+
},
|
|
2297
|
+
{
|
|
2298
|
+
in: "cookie",
|
|
2299
|
+
name: "ory_kratos_session",
|
|
2300
|
+
type: "apiKey"
|
|
2301
|
+
}
|
|
2302
|
+
],
|
|
2303
|
+
url: "/tasks/{id}/attempts/{n}/messages",
|
|
2304
|
+
...options,
|
|
2305
|
+
headers: {
|
|
2306
|
+
"Content-Type": "application/json",
|
|
2307
|
+
...options.headers
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
2310
|
+
/**
|
|
2042
2311
|
* List all problem types used in API error responses (RFC 9457).
|
|
2043
2312
|
*/
|
|
2044
2313
|
var listProblemTypes = (options) => (options?.client ?? client).get({
|
|
@@ -2810,7 +3079,7 @@ K512[1];
|
|
|
2810
3079
|
* To break sha256 using birthday attack, attackers need to try 2^128 hashes.
|
|
2811
3080
|
* BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
2812
3081
|
*/
|
|
2813
|
-
var sha256 = /* @__PURE__ */ createHasher(() => new SHA256());
|
|
3082
|
+
var sha256$1 = /* @__PURE__ */ createHasher(() => new SHA256());
|
|
2814
3083
|
//#endregion
|
|
2815
3084
|
//#region ../../node_modules/.pnpm/multiformats@13.4.2/node_modules/multiformats/dist/src/bytes.js
|
|
2816
3085
|
function equals$1(aa, bb) {
|
|
@@ -3027,12 +3296,12 @@ var Codec = class {
|
|
|
3027
3296
|
return this.decoder.decode(input);
|
|
3028
3297
|
}
|
|
3029
3298
|
};
|
|
3030
|
-
function from({ name, prefix, encode, decode }) {
|
|
3299
|
+
function from$1({ name, prefix, encode, decode }) {
|
|
3031
3300
|
return new Codec(name, prefix, encode, decode);
|
|
3032
3301
|
}
|
|
3033
3302
|
function baseX({ name, prefix, alphabet }) {
|
|
3034
3303
|
const { encode, decode } = _brrp__multiformats_scope_baseX(alphabet, name);
|
|
3035
|
-
return from({
|
|
3304
|
+
return from$1({
|
|
3036
3305
|
prefix,
|
|
3037
3306
|
name,
|
|
3038
3307
|
encode,
|
|
@@ -3059,7 +3328,7 @@ function decode$3(string, alphabetIdx, bitsPerChar, name) {
|
|
|
3059
3328
|
if (bits >= bitsPerChar || (255 & buffer << 8 - bits) !== 0) throw new SyntaxError("Unexpected end of data");
|
|
3060
3329
|
return out;
|
|
3061
3330
|
}
|
|
3062
|
-
function encode$
|
|
3331
|
+
function encode$2(data, alphabet, bitsPerChar) {
|
|
3063
3332
|
const pad = alphabet[alphabet.length - 1] === "=";
|
|
3064
3333
|
const mask = (1 << bitsPerChar) - 1;
|
|
3065
3334
|
let out = "";
|
|
@@ -3087,11 +3356,11 @@ function createAlphabetIdx(alphabet) {
|
|
|
3087
3356
|
*/
|
|
3088
3357
|
function rfc4648({ name, prefix, bitsPerChar, alphabet }) {
|
|
3089
3358
|
const alphabetIdx = createAlphabetIdx(alphabet);
|
|
3090
|
-
return from({
|
|
3359
|
+
return from$1({
|
|
3091
3360
|
prefix,
|
|
3092
3361
|
name,
|
|
3093
3362
|
encode(input) {
|
|
3094
|
-
return encode$
|
|
3363
|
+
return encode$2(input, alphabet, bitsPerChar);
|
|
3095
3364
|
},
|
|
3096
3365
|
decode(input) {
|
|
3097
3366
|
return decode$3(input, alphabetIdx, bitsPerChar, name);
|
|
@@ -3180,14 +3449,14 @@ baseX({
|
|
|
3180
3449
|
});
|
|
3181
3450
|
//#endregion
|
|
3182
3451
|
//#region ../../node_modules/.pnpm/multiformats@13.4.2/node_modules/multiformats/dist/src/vendor/varint.js
|
|
3183
|
-
var encode_1 = encode;
|
|
3452
|
+
var encode_1 = encode$1;
|
|
3184
3453
|
var MSB = 128, MSBALL = -128, INT = Math.pow(2, 31);
|
|
3185
3454
|
/**
|
|
3186
3455
|
* @param {number} num
|
|
3187
3456
|
* @param {number[]} out
|
|
3188
3457
|
* @param {number} offset
|
|
3189
3458
|
*/
|
|
3190
|
-
function encode(num, out, offset) {
|
|
3459
|
+
function encode$1(num, out, offset) {
|
|
3191
3460
|
out = out || [];
|
|
3192
3461
|
offset = offset || 0;
|
|
3193
3462
|
var oldOffset = offset;
|
|
@@ -3200,7 +3469,7 @@ function encode(num, out, offset) {
|
|
|
3200
3469
|
num >>>= 7;
|
|
3201
3470
|
}
|
|
3202
3471
|
out[offset] = num | 0;
|
|
3203
|
-
encode.bytes = offset - oldOffset + 1;
|
|
3472
|
+
encode$1.bytes = offset - oldOffset + 1;
|
|
3204
3473
|
return out;
|
|
3205
3474
|
}
|
|
3206
3475
|
var decode$2 = read;
|
|
@@ -3623,7 +3892,7 @@ function buildCanonicalInput(entryType, title, content, tags) {
|
|
|
3623
3892
|
*/
|
|
3624
3893
|
function computeCanonicalHash(entryType, title, content, tags) {
|
|
3625
3894
|
const input = buildCanonicalInput(entryType, title, content, tags);
|
|
3626
|
-
return sha256(new TextEncoder().encode(input));
|
|
3895
|
+
return sha256$1(new TextEncoder().encode(input));
|
|
3627
3896
|
}
|
|
3628
3897
|
/**
|
|
3629
3898
|
* Compute a CIDv1 content identifier for a diary entry.
|
|
@@ -4070,25 +4339,104 @@ etc.sha512Sync = (...m) => {
|
|
|
4070
4339
|
return hash.digest();
|
|
4071
4340
|
};
|
|
4072
4341
|
//#endregion
|
|
4073
|
-
//#region ../../node_modules/.pnpm/
|
|
4074
|
-
var
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4342
|
+
//#region ../../node_modules/.pnpm/multiformats@13.4.2/node_modules/multiformats/dist/src/codecs/json.js
|
|
4343
|
+
var textEncoder$1 = new TextEncoder();
|
|
4344
|
+
new TextDecoder();
|
|
4345
|
+
function encode(node) {
|
|
4346
|
+
return textEncoder$1.encode(JSON.stringify(node));
|
|
4347
|
+
}
|
|
4348
|
+
//#endregion
|
|
4349
|
+
//#region ../../node_modules/.pnpm/multiformats@13.4.2/node_modules/multiformats/dist/src/hashes/hasher.js
|
|
4350
|
+
var DEFAULT_MIN_DIGEST_LENGTH = 20;
|
|
4351
|
+
function from({ name, code, encode, minDigestLength, maxDigestLength }) {
|
|
4352
|
+
return new Hasher(name, code, encode, minDigestLength, maxDigestLength);
|
|
4353
|
+
}
|
|
4354
|
+
/**
|
|
4355
|
+
* Hasher represents a hashing algorithm implementation that produces as
|
|
4356
|
+
* `MultihashDigest`.
|
|
4357
|
+
*/
|
|
4358
|
+
var Hasher = class {
|
|
4359
|
+
name;
|
|
4360
|
+
code;
|
|
4361
|
+
encode;
|
|
4362
|
+
minDigestLength;
|
|
4363
|
+
maxDigestLength;
|
|
4364
|
+
constructor(name, code, encode, minDigestLength, maxDigestLength) {
|
|
4365
|
+
this.name = name;
|
|
4366
|
+
this.code = code;
|
|
4367
|
+
this.encode = encode;
|
|
4368
|
+
this.minDigestLength = minDigestLength ?? DEFAULT_MIN_DIGEST_LENGTH;
|
|
4369
|
+
this.maxDigestLength = maxDigestLength;
|
|
4370
|
+
}
|
|
4371
|
+
digest(input, options) {
|
|
4372
|
+
if (options?.truncate != null) {
|
|
4373
|
+
if (options.truncate < this.minDigestLength) throw new Error(`Invalid truncate option, must be greater than or equal to ${this.minDigestLength}`);
|
|
4374
|
+
if (this.maxDigestLength != null && options.truncate > this.maxDigestLength) throw new Error(`Invalid truncate option, must be less than or equal to ${this.maxDigestLength}`);
|
|
4375
|
+
}
|
|
4376
|
+
if (input instanceof Uint8Array) {
|
|
4377
|
+
const result = this.encode(input);
|
|
4378
|
+
if (result instanceof Uint8Array) return createDigest(result, this.code, options?.truncate);
|
|
4379
|
+
return result.then((digest) => createDigest(digest, this.code, options?.truncate));
|
|
4380
|
+
} else throw Error("Unknown type, must be binary type");
|
|
4381
|
+
}
|
|
4382
|
+
};
|
|
4383
|
+
/**
|
|
4384
|
+
* Create a Digest from the passed uint8array and code, optionally truncating it
|
|
4385
|
+
* first.
|
|
4386
|
+
*/
|
|
4387
|
+
function createDigest(digest, code, truncate) {
|
|
4388
|
+
if (truncate != null && truncate !== digest.byteLength) {
|
|
4389
|
+
if (truncate > digest.byteLength) throw new Error(`Invalid truncate option, must be less than or equal to ${digest.byteLength}`);
|
|
4390
|
+
digest = digest.subarray(0, truncate);
|
|
4391
|
+
}
|
|
4392
|
+
return create(code, digest);
|
|
4393
|
+
}
|
|
4394
|
+
//#endregion
|
|
4395
|
+
//#region ../../node_modules/.pnpm/multiformats@13.4.2/node_modules/multiformats/dist/src/hashes/sha2.js
|
|
4396
|
+
var sha256 = from({
|
|
4397
|
+
name: "sha2-256",
|
|
4398
|
+
code: 18,
|
|
4399
|
+
encode: (input) => coerce(crypto.createHash("sha256").update(input).digest())
|
|
4400
|
+
});
|
|
4401
|
+
from({
|
|
4402
|
+
name: "sha2-512",
|
|
4403
|
+
code: 19,
|
|
4404
|
+
encode: (input) => coerce(crypto.createHash("sha512").update(input).digest())
|
|
4405
|
+
});
|
|
4406
|
+
//#endregion
|
|
4407
|
+
//#region ../crypto-service/src/json-cid.ts
|
|
4408
|
+
/**
|
|
4409
|
+
* Generic JSON CID — CIDv1 for arbitrary JSON-serialisable values.
|
|
4410
|
+
*
|
|
4411
|
+
* Uses the dag-json codec and sha2-256, producing a base32lower CIDv1.
|
|
4412
|
+
* Suitable for content-addressing task inputs, schema objects, and other
|
|
4413
|
+
* JSON payloads that don't need diary-entry canonical normalisation.
|
|
4414
|
+
*/
|
|
4415
|
+
async function computeJsonCid(value) {
|
|
4416
|
+
const bytes = encode(value);
|
|
4417
|
+
const hash = await sha256.digest(bytes);
|
|
4418
|
+
return CID.create(1, 512, hash).toString();
|
|
4419
|
+
}
|
|
4420
|
+
//#endregion
|
|
4421
|
+
//#region ../../node_modules/.pnpm/cborg@4.5.8/node_modules/cborg/lib/is.js
|
|
4422
|
+
var objectTypeNames = [
|
|
4423
|
+
"Object",
|
|
4424
|
+
"RegExp",
|
|
4425
|
+
"Date",
|
|
4426
|
+
"Error",
|
|
4427
|
+
"Map",
|
|
4428
|
+
"Set",
|
|
4429
|
+
"WeakMap",
|
|
4430
|
+
"WeakSet",
|
|
4431
|
+
"ArrayBuffer",
|
|
4432
|
+
"SharedArrayBuffer",
|
|
4433
|
+
"DataView",
|
|
4434
|
+
"Promise",
|
|
4435
|
+
"URL",
|
|
4436
|
+
"HTMLElement",
|
|
4437
|
+
"Int8Array",
|
|
4438
|
+
"Uint8ClampedArray",
|
|
4439
|
+
"Int16Array",
|
|
4092
4440
|
"Uint16Array",
|
|
4093
4441
|
"Int32Array",
|
|
4094
4442
|
"Uint32Array",
|
|
@@ -6244,6 +6592,112 @@ function createSigningRequestsNamespace(context) {
|
|
|
6244
6592
|
};
|
|
6245
6593
|
}
|
|
6246
6594
|
//#endregion
|
|
6595
|
+
//#region ../sdk/src/namespaces/tasks.ts
|
|
6596
|
+
function createTasksNamespace(context) {
|
|
6597
|
+
const { client, auth } = context;
|
|
6598
|
+
return {
|
|
6599
|
+
async list(query) {
|
|
6600
|
+
return unwrapResult(await listTasks({
|
|
6601
|
+
client,
|
|
6602
|
+
auth,
|
|
6603
|
+
query
|
|
6604
|
+
}));
|
|
6605
|
+
},
|
|
6606
|
+
async create(body) {
|
|
6607
|
+
return unwrapResult(await createTask({
|
|
6608
|
+
client,
|
|
6609
|
+
auth,
|
|
6610
|
+
body
|
|
6611
|
+
}));
|
|
6612
|
+
},
|
|
6613
|
+
async get(id) {
|
|
6614
|
+
return unwrapResult(await getTask({
|
|
6615
|
+
client,
|
|
6616
|
+
auth,
|
|
6617
|
+
path: { id }
|
|
6618
|
+
}));
|
|
6619
|
+
},
|
|
6620
|
+
async claim(id, body) {
|
|
6621
|
+
return unwrapResult(await claimTask({
|
|
6622
|
+
client,
|
|
6623
|
+
auth,
|
|
6624
|
+
path: { id },
|
|
6625
|
+
body
|
|
6626
|
+
}));
|
|
6627
|
+
},
|
|
6628
|
+
async heartbeat(id, n, body) {
|
|
6629
|
+
return unwrapResult(await taskHeartbeat({
|
|
6630
|
+
client,
|
|
6631
|
+
auth,
|
|
6632
|
+
path: {
|
|
6633
|
+
id,
|
|
6634
|
+
n
|
|
6635
|
+
},
|
|
6636
|
+
body
|
|
6637
|
+
}));
|
|
6638
|
+
},
|
|
6639
|
+
async complete(id, n, body) {
|
|
6640
|
+
return unwrapResult(await completeTask({
|
|
6641
|
+
client,
|
|
6642
|
+
auth,
|
|
6643
|
+
path: {
|
|
6644
|
+
id,
|
|
6645
|
+
n
|
|
6646
|
+
},
|
|
6647
|
+
body
|
|
6648
|
+
}));
|
|
6649
|
+
},
|
|
6650
|
+
async fail(id, n, body) {
|
|
6651
|
+
return unwrapResult(await failTask({
|
|
6652
|
+
client,
|
|
6653
|
+
auth,
|
|
6654
|
+
path: {
|
|
6655
|
+
id,
|
|
6656
|
+
n
|
|
6657
|
+
},
|
|
6658
|
+
body
|
|
6659
|
+
}));
|
|
6660
|
+
},
|
|
6661
|
+
async cancel(id, body) {
|
|
6662
|
+
return unwrapResult(await cancelTask({
|
|
6663
|
+
client,
|
|
6664
|
+
auth,
|
|
6665
|
+
path: { id },
|
|
6666
|
+
body
|
|
6667
|
+
}));
|
|
6668
|
+
},
|
|
6669
|
+
async listAttempts(id) {
|
|
6670
|
+
return unwrapResult(await listTaskAttempts({
|
|
6671
|
+
client,
|
|
6672
|
+
auth,
|
|
6673
|
+
path: { id }
|
|
6674
|
+
}));
|
|
6675
|
+
},
|
|
6676
|
+
async listMessages(id, n, query) {
|
|
6677
|
+
return unwrapResult(await listTaskMessages({
|
|
6678
|
+
client,
|
|
6679
|
+
auth,
|
|
6680
|
+
path: {
|
|
6681
|
+
id,
|
|
6682
|
+
n
|
|
6683
|
+
},
|
|
6684
|
+
query
|
|
6685
|
+
}));
|
|
6686
|
+
},
|
|
6687
|
+
async appendMessages(id, n, body) {
|
|
6688
|
+
return unwrapResult(await appendTaskMessages({
|
|
6689
|
+
client,
|
|
6690
|
+
auth,
|
|
6691
|
+
path: {
|
|
6692
|
+
id,
|
|
6693
|
+
n
|
|
6694
|
+
},
|
|
6695
|
+
body
|
|
6696
|
+
}));
|
|
6697
|
+
}
|
|
6698
|
+
};
|
|
6699
|
+
}
|
|
6700
|
+
//#endregion
|
|
6247
6701
|
//#region ../sdk/src/namespaces/teams.ts
|
|
6248
6702
|
function createTeamsNamespace(context) {
|
|
6249
6703
|
const { client, auth } = context;
|
|
@@ -6375,6 +6829,7 @@ function createAgent(options) {
|
|
|
6375
6829
|
legreffier: createLegreffierNamespace(context),
|
|
6376
6830
|
problems: createProblemsNamespace(context),
|
|
6377
6831
|
teams: createTeamsNamespace(context),
|
|
6832
|
+
tasks: createTasksNamespace(context),
|
|
6378
6833
|
client,
|
|
6379
6834
|
getToken: () => tokenManager.getToken()
|
|
6380
6835
|
};
|
|
@@ -7113,6 +7568,24 @@ function renderPhase6Markdown(pack) {
|
|
|
7113
7568
|
* These tools run on the host (not in the VM) via the MoltNet SDK,
|
|
7114
7569
|
* so agent credentials never touch the VM filesystem.
|
|
7115
7570
|
*/
|
|
7571
|
+
/**
|
|
7572
|
+
* Baseline env keys forwarded to host-exec child processes.
|
|
7573
|
+
* Callers can extend this set at sandbox startup via `MoltNetToolsConfig.hostExecBaseEnv`.
|
|
7574
|
+
*/
|
|
7575
|
+
var HOST_EXEC_DEFAULT_BASE_ENV = new Set([
|
|
7576
|
+
"PATH",
|
|
7577
|
+
"HOME",
|
|
7578
|
+
"LANG",
|
|
7579
|
+
"LC_ALL",
|
|
7580
|
+
"TMPDIR",
|
|
7581
|
+
"GIT_CONFIG_GLOBAL",
|
|
7582
|
+
"MOLTNET_CREDENTIALS_PATH",
|
|
7583
|
+
"GIT_AUTHOR_NAME",
|
|
7584
|
+
"GIT_AUTHOR_EMAIL",
|
|
7585
|
+
"GIT_COMMITTER_NAME",
|
|
7586
|
+
"GIT_COMMITTER_EMAIL",
|
|
7587
|
+
"SSH_AUTH_SOCK"
|
|
7588
|
+
]);
|
|
7116
7589
|
function ensureConnected(config) {
|
|
7117
7590
|
const agent = config.getAgent();
|
|
7118
7591
|
const diaryId = config.getDiaryId();
|
|
@@ -7126,303 +7599,548 @@ function ensureConnected(config) {
|
|
|
7126
7599
|
* Create all MoltNet tool definitions, ready to pass to `pi.registerTool()`.
|
|
7127
7600
|
*/
|
|
7128
7601
|
function createMoltNetTools(config) {
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
expandEntries: Type.Optional(Type.Boolean({ description: "Include full expanded entries" }))
|
|
7137
|
-
}),
|
|
7138
|
-
async execute(_id, params) {
|
|
7139
|
-
const { agent } = ensureConnected(config);
|
|
7140
|
-
const pack = await agent.packs.get(params.packId, { expand: params.expandEntries ? "entries" : void 0 });
|
|
7141
|
-
return {
|
|
7142
|
-
content: [{
|
|
7143
|
-
type: "text",
|
|
7144
|
-
text: JSON.stringify(pack, null, 2)
|
|
7145
|
-
}],
|
|
7146
|
-
details: {}
|
|
7147
|
-
};
|
|
7148
|
-
}
|
|
7602
|
+
const getPack = defineTool({
|
|
7603
|
+
name: "moltnet_pack_get",
|
|
7604
|
+
label: "Get MoltNet Pack",
|
|
7605
|
+
description: "Get a context pack by ID. Optionally expand included entries.",
|
|
7606
|
+
parameters: Type.Object({
|
|
7607
|
+
packId: Type.String({ description: "Context pack ID" }),
|
|
7608
|
+
expandEntries: Type.Optional(Type.Boolean({ description: "Include full expanded entries" }))
|
|
7149
7609
|
}),
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
type: "text",
|
|
7175
|
-
text: JSON.stringify(pack, null, 2)
|
|
7176
|
-
}],
|
|
7177
|
-
details: {}
|
|
7178
|
-
};
|
|
7179
|
-
}
|
|
7610
|
+
async execute(_id, params) {
|
|
7611
|
+
const { agent } = ensureConnected(config);
|
|
7612
|
+
const pack = await agent.packs.get(params.packId, { expand: params.expandEntries ? "entries" : void 0 });
|
|
7613
|
+
return {
|
|
7614
|
+
content: [{
|
|
7615
|
+
type: "text",
|
|
7616
|
+
text: JSON.stringify(pack, null, 2)
|
|
7617
|
+
}],
|
|
7618
|
+
details: {}
|
|
7619
|
+
};
|
|
7620
|
+
}
|
|
7621
|
+
});
|
|
7622
|
+
const createPack = defineTool({
|
|
7623
|
+
name: "moltnet_pack_create",
|
|
7624
|
+
label: "Create MoltNet Pack",
|
|
7625
|
+
description: "Persist a curated context pack. Entries are caller-ranked (lower rank = more prominent). Recipe/prompt/selection_rationale belong in params. Defaults to pinned=false — packs in the attribution pipeline are ephemeral unless the caller explicitly opts in.",
|
|
7626
|
+
parameters: Type.Object({
|
|
7627
|
+
entries: Type.Array(Type.Object({
|
|
7628
|
+
entryId: Type.String({ description: "Diary entry UUID" }),
|
|
7629
|
+
rank: Type.Number({ description: "Rank (1..N, lower = more prominent)" })
|
|
7630
|
+
}), { description: "Selected entries with their ranks" }),
|
|
7631
|
+
params: Type.Optional(Type.Record(Type.String(), Type.Unknown(), { description: "Free-form recipe parameters (recipe name, prompt, selection rationale, etc.)" })),
|
|
7632
|
+
tokenBudget: Type.Optional(Type.Number({ description: "Soft token budget recorded on the pack (optional)" })),
|
|
7633
|
+
pinned: Type.Optional(Type.Boolean({ description: "Pin the pack against retention policy (default false)" }))
|
|
7180
7634
|
}),
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
})
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
}],
|
|
7208
|
-
details: {}
|
|
7209
|
-
};
|
|
7210
|
-
}
|
|
7635
|
+
async execute(_id, params) {
|
|
7636
|
+
const { agent, diaryId } = ensureConnected(config);
|
|
7637
|
+
const pack = await agent.packs.create(diaryId, {
|
|
7638
|
+
packType: "custom",
|
|
7639
|
+
params: params.params ?? {},
|
|
7640
|
+
entries: params.entries,
|
|
7641
|
+
tokenBudget: params.tokenBudget,
|
|
7642
|
+
pinned: params.pinned ?? false
|
|
7643
|
+
});
|
|
7644
|
+
return {
|
|
7645
|
+
content: [{
|
|
7646
|
+
type: "text",
|
|
7647
|
+
text: JSON.stringify(pack, null, 2)
|
|
7648
|
+
}],
|
|
7649
|
+
details: {}
|
|
7650
|
+
};
|
|
7651
|
+
}
|
|
7652
|
+
});
|
|
7653
|
+
const getPackProvenance = defineTool({
|
|
7654
|
+
name: "moltnet_pack_provenance",
|
|
7655
|
+
label: "Get MoltNet Pack Provenance",
|
|
7656
|
+
description: "Get the provenance graph for a context pack by ID or CID.",
|
|
7657
|
+
parameters: Type.Object({
|
|
7658
|
+
packId: Type.Optional(Type.String({ description: "Context pack ID" })),
|
|
7659
|
+
packCid: Type.Optional(Type.String({ description: "Context pack CID" })),
|
|
7660
|
+
depth: Type.Optional(Type.Number({ description: "Supersession ancestry depth to include (default 2)" }))
|
|
7211
7661
|
}),
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
}
|
|
7662
|
+
async execute(_id, params) {
|
|
7663
|
+
const { agent } = ensureConnected(config);
|
|
7664
|
+
if (!params.packId && !params.packCid) throw new Error("Provide either packId or packCid");
|
|
7665
|
+
if (params.packId && params.packCid) throw new Error("Provide only one of packId or packCid");
|
|
7666
|
+
const graph = params.packId ? await agent.packs.getProvenance(params.packId, { depth: params.depth ?? 2 }) : await agent.packs.getProvenanceByCid(params.packCid, { depth: params.depth ?? 2 });
|
|
7667
|
+
const payload = {
|
|
7668
|
+
metadata: graph.metadata,
|
|
7669
|
+
counts: {
|
|
7670
|
+
nodes: graph.nodes.length,
|
|
7671
|
+
edges: graph.edges.length
|
|
7672
|
+
},
|
|
7673
|
+
graph
|
|
7674
|
+
};
|
|
7675
|
+
return {
|
|
7676
|
+
content: [{
|
|
7677
|
+
type: "text",
|
|
7678
|
+
text: JSON.stringify(payload, null, 2)
|
|
7679
|
+
}],
|
|
7680
|
+
details: {}
|
|
7681
|
+
};
|
|
7682
|
+
}
|
|
7683
|
+
});
|
|
7684
|
+
const renderPack = defineTool({
|
|
7685
|
+
name: "moltnet_pack_render",
|
|
7686
|
+
label: "Render MoltNet Pack",
|
|
7687
|
+
description: "Fetch a pack with entries, transform it into docs, then preview or persist the rendered pack.",
|
|
7688
|
+
parameters: Type.Object({
|
|
7689
|
+
packId: Type.String({ description: "Context pack ID" }),
|
|
7690
|
+
renderMethod: Type.Optional(Type.String({ description: "Render method label. Defaults to pi:pack-to-docs-v1" })),
|
|
7691
|
+
markdown: Type.Optional(Type.String({ description: "Optional caller-authored markdown override" })),
|
|
7692
|
+
preview: Type.Optional(Type.Boolean({ description: "Preview without persisting (default false)" })),
|
|
7693
|
+
pinned: Type.Optional(Type.Boolean({ description: "Persist the rendered pack as pinned (default false)" }))
|
|
7244
7694
|
}),
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
renderMethod
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
}
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7695
|
+
async execute(_id, params) {
|
|
7696
|
+
const { agent } = ensureConnected(config);
|
|
7697
|
+
const renderMethod = params.renderMethod ?? "pi:pack-to-docs-v1";
|
|
7698
|
+
let renderedMarkdown = params.markdown;
|
|
7699
|
+
if (!renderedMarkdown && !renderMethod.startsWith("server:")) renderedMarkdown = renderPhase6Markdown(await agent.packs.get(params.packId, { expand: "entries" }));
|
|
7700
|
+
const result = params.preview ?? false ? await agent.packs.previewRendered(params.packId, {
|
|
7701
|
+
renderMethod,
|
|
7702
|
+
renderedMarkdown
|
|
7703
|
+
}) : await agent.packs.render(params.packId, {
|
|
7704
|
+
renderMethod,
|
|
7705
|
+
renderedMarkdown,
|
|
7706
|
+
pinned: params.pinned
|
|
7707
|
+
});
|
|
7708
|
+
return {
|
|
7709
|
+
content: [{
|
|
7710
|
+
type: "text",
|
|
7711
|
+
text: JSON.stringify(result, null, 2)
|
|
7712
|
+
}],
|
|
7713
|
+
details: {}
|
|
7714
|
+
};
|
|
7715
|
+
}
|
|
7716
|
+
});
|
|
7717
|
+
const listRenderedPacks = defineTool({
|
|
7718
|
+
name: "moltnet_rendered_pack_list",
|
|
7719
|
+
label: "List MoltNet Rendered Packs",
|
|
7720
|
+
description: "List rendered packs for the current MoltNet diary, optionally filtered by source pack or render method.",
|
|
7721
|
+
parameters: Type.Object({
|
|
7722
|
+
sourcePackId: Type.Optional(Type.String({ description: "Filter by source pack ID" })),
|
|
7723
|
+
renderMethod: Type.Optional(Type.String({ description: "Filter by render method" })),
|
|
7724
|
+
limit: Type.Optional(Type.Number({ description: "Max results (default 10)" })),
|
|
7725
|
+
offset: Type.Optional(Type.Number({ description: "Offset for pagination (default 0)" }))
|
|
7271
7726
|
}),
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7727
|
+
async execute(_id, params) {
|
|
7728
|
+
const { agent, diaryId } = ensureConnected(config);
|
|
7729
|
+
const rendered = await agent.packs.listRendered(diaryId, {
|
|
7730
|
+
sourcePackId: params.sourcePackId,
|
|
7731
|
+
renderMethod: params.renderMethod,
|
|
7732
|
+
limit: params.limit ?? 10,
|
|
7733
|
+
offset: params.offset ?? 0
|
|
7734
|
+
});
|
|
7735
|
+
return {
|
|
7736
|
+
content: [{
|
|
7737
|
+
type: "text",
|
|
7738
|
+
text: JSON.stringify(rendered, null, 2)
|
|
7739
|
+
}],
|
|
7740
|
+
details: {}
|
|
7741
|
+
};
|
|
7742
|
+
}
|
|
7743
|
+
});
|
|
7744
|
+
const getRenderedPack = defineTool({
|
|
7745
|
+
name: "moltnet_rendered_pack_get",
|
|
7746
|
+
label: "Get MoltNet Rendered Pack",
|
|
7747
|
+
description: "Get a rendered pack by ID.",
|
|
7748
|
+
parameters: Type.Object({ renderedPackId: Type.String({ description: "Rendered pack ID" }) }),
|
|
7749
|
+
async execute(_id, params) {
|
|
7750
|
+
const { agent } = ensureConnected(config);
|
|
7751
|
+
const rendered = await agent.packs.getRendered(params.renderedPackId);
|
|
7752
|
+
return {
|
|
7753
|
+
content: [{
|
|
7754
|
+
type: "text",
|
|
7755
|
+
text: JSON.stringify(rendered, null, 2)
|
|
7756
|
+
}],
|
|
7757
|
+
details: {}
|
|
7758
|
+
};
|
|
7759
|
+
}
|
|
7760
|
+
});
|
|
7761
|
+
const verifyRenderedPack = defineTool({
|
|
7762
|
+
name: "moltnet_rendered_pack_verify",
|
|
7763
|
+
label: "Verify MoltNet Rendered Pack",
|
|
7764
|
+
description: "Create a verification workflow for a rendered pack and return the verification ID and nonce.",
|
|
7765
|
+
parameters: Type.Object({
|
|
7766
|
+
renderedPackId: Type.String({ description: "Rendered pack ID" }),
|
|
7767
|
+
nonce: Type.Optional(Type.String({ description: "Caller-supplied idempotency nonce. Generated automatically if omitted." }))
|
|
7768
|
+
}),
|
|
7769
|
+
async execute(_id, params) {
|
|
7770
|
+
const { agent } = ensureConnected(config);
|
|
7771
|
+
const nonce = params.nonce ?? randomUUID();
|
|
7772
|
+
const verification = await agent.packs.verifyRendered(params.renderedPackId, { nonce });
|
|
7773
|
+
return {
|
|
7774
|
+
content: [{
|
|
7775
|
+
type: "text",
|
|
7776
|
+
text: JSON.stringify({
|
|
7777
|
+
...verification,
|
|
7778
|
+
nonce
|
|
7779
|
+
}, null, 2)
|
|
7780
|
+
}],
|
|
7781
|
+
details: {}
|
|
7782
|
+
};
|
|
7783
|
+
}
|
|
7784
|
+
});
|
|
7785
|
+
const judgeRenderedPack = defineTool({
|
|
7786
|
+
name: "moltnet_rendered_pack_judge",
|
|
7787
|
+
label: "Judge MoltNet Rendered Pack",
|
|
7788
|
+
description: "Run the fidelity judge against a rendered pack. Local mode (no nonce): fetch the rendered pack + its source pack with entries, judge locally, return scores. Proctored mode (nonce): claim the verification payload from the API, judge, and submit scores with a Pi judge-recipe CID.",
|
|
7789
|
+
parameters: Type.Object({
|
|
7790
|
+
renderedPackId: Type.String({ description: "Rendered pack ID" }),
|
|
7791
|
+
nonce: Type.Optional(Type.String({ description: "Verification nonce from moltnet_rendered_pack_verify. If set, runs proctored mode and submits scores. If omitted, runs local mode and does not submit." })),
|
|
7792
|
+
rubric: Type.Optional(Type.String({ description: "Custom rubric override (local mode only). Defaults to the built-in rubric when omitted." }))
|
|
7793
|
+
}),
|
|
7794
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
7795
|
+
const { agent } = ensureConnected(config);
|
|
7796
|
+
const model = ctx?.model;
|
|
7797
|
+
if (!model) throw new Error("No active model in pi session — cannot run the fidelity judge.");
|
|
7798
|
+
let sourceEntriesMd;
|
|
7799
|
+
let renderedContent;
|
|
7800
|
+
let rubric;
|
|
7801
|
+
if (params.nonce) {
|
|
7802
|
+
if (params.rubric) throw new Error("`rubric` is only supported in local mode (omit `nonce`).");
|
|
7803
|
+
const claim = await agent.packs.claimVerification(params.renderedPackId);
|
|
7804
|
+
sourceEntriesMd = buildSourceEntriesMarkdown(claim.sourceEntries);
|
|
7805
|
+
renderedContent = claim.renderedContent;
|
|
7806
|
+
rubric = claim.rubric?.trim() ? claim.rubric : DEFAULT_RUBRIC;
|
|
7807
|
+
} else {
|
|
7279
7808
|
const rendered = await agent.packs.getRendered(params.renderedPackId);
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
};
|
|
7809
|
+
if (!rendered.content?.trim()) throw new Error(`rendered pack ${params.renderedPackId} has empty content`);
|
|
7810
|
+
const sourcePack = await agent.packs.get(rendered.sourcePackId, { expand: "entries" });
|
|
7811
|
+
if (!sourcePack.entries || sourcePack.entries.length === 0) throw new Error(`source pack ${rendered.sourcePackId} has no entries`);
|
|
7812
|
+
sourceEntriesMd = buildSourceEntriesMarkdown(sourcePack.entries.map((entry) => ({
|
|
7813
|
+
title: entry.entry.title,
|
|
7814
|
+
content: entry.entry.content
|
|
7815
|
+
})));
|
|
7816
|
+
renderedContent = rendered.content;
|
|
7817
|
+
rubric = params.rubric?.trim() ? params.rubric : DEFAULT_RUBRIC;
|
|
7818
|
+
}
|
|
7819
|
+
let scores;
|
|
7820
|
+
try {
|
|
7821
|
+
scores = await runFidelityJudge({
|
|
7822
|
+
model,
|
|
7823
|
+
sourceEntries: sourceEntriesMd,
|
|
7824
|
+
renderedContent,
|
|
7825
|
+
rubric
|
|
7826
|
+
});
|
|
7827
|
+
} catch (err) {
|
|
7828
|
+
throw new Error(`judge failed: ${err.message ?? String(err)}`);
|
|
7287
7829
|
}
|
|
7830
|
+
if (!params.nonce) return {
|
|
7831
|
+
content: [{
|
|
7832
|
+
type: "text",
|
|
7833
|
+
text: JSON.stringify({
|
|
7834
|
+
mode: "local",
|
|
7835
|
+
renderedPackId: params.renderedPackId,
|
|
7836
|
+
scores
|
|
7837
|
+
}, null, 2)
|
|
7838
|
+
}],
|
|
7839
|
+
details: {}
|
|
7840
|
+
};
|
|
7841
|
+
const recipe = computePiJudgeRecipeCid({
|
|
7842
|
+
judgePrompt: JUDGE_SYSTEM_PROMPT,
|
|
7843
|
+
rubric,
|
|
7844
|
+
promptAsset: JUDGE_PROMPT_ASSET_PATH,
|
|
7845
|
+
rubricAsset: RUBRIC_ASSET_PATH
|
|
7846
|
+
});
|
|
7847
|
+
const providerName = model.provider ?? "pi";
|
|
7848
|
+
const modelId = model.id ?? "unknown";
|
|
7849
|
+
const submit = await agent.packs.submitVerification(params.renderedPackId, {
|
|
7850
|
+
nonce: params.nonce,
|
|
7851
|
+
coverage: scores.coverage,
|
|
7852
|
+
grounding: scores.grounding,
|
|
7853
|
+
faithfulness: scores.faithfulness,
|
|
7854
|
+
transcript: scores.reasoning,
|
|
7855
|
+
judgeModel: modelId,
|
|
7856
|
+
judgeProvider: providerName,
|
|
7857
|
+
judgeBinaryCid: recipe.cid
|
|
7858
|
+
});
|
|
7859
|
+
return {
|
|
7860
|
+
content: [{
|
|
7861
|
+
type: "text",
|
|
7862
|
+
text: JSON.stringify({
|
|
7863
|
+
mode: "proctored",
|
|
7864
|
+
renderedPackId: params.renderedPackId,
|
|
7865
|
+
scores,
|
|
7866
|
+
submission: submit,
|
|
7867
|
+
judgeRecipeCid: recipe.cid,
|
|
7868
|
+
judgeRecipeManifest: recipe.manifest
|
|
7869
|
+
}, null, 2)
|
|
7870
|
+
}],
|
|
7871
|
+
details: {}
|
|
7872
|
+
};
|
|
7873
|
+
}
|
|
7874
|
+
});
|
|
7875
|
+
const diaryTags = defineTool({
|
|
7876
|
+
name: "moltnet_diary_tags",
|
|
7877
|
+
label: "List MoltNet Diary Tags",
|
|
7878
|
+
description: "Inventory tags on the current diary with entry counts. Cheap reconnaissance before committing to a search or list — use it to discover scope prefixes and cluster sizes. Optional prefix/minCount/entryTypes filters narrow the result.",
|
|
7879
|
+
parameters: Type.Object({
|
|
7880
|
+
prefix: Type.Optional(Type.String({ description: "Filter to tags starting with this prefix (e.g. \"scope:\")" })),
|
|
7881
|
+
minCount: Type.Optional(Type.Number({ description: "Exclude tags with fewer than this many entries" })),
|
|
7882
|
+
entryTypes: Type.Optional(Type.Array(Type.Union([
|
|
7883
|
+
Type.Literal("episodic"),
|
|
7884
|
+
Type.Literal("semantic"),
|
|
7885
|
+
Type.Literal("procedural"),
|
|
7886
|
+
Type.Literal("reflection"),
|
|
7887
|
+
Type.Literal("identity"),
|
|
7888
|
+
Type.Literal("soul")
|
|
7889
|
+
]), { description: "Scope the tag count to these entry types" }))
|
|
7288
7890
|
}),
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7891
|
+
async execute(_id, params) {
|
|
7892
|
+
const { agent, diaryId } = ensureConnected(config);
|
|
7893
|
+
const result = await agent.diaries.tags(diaryId, {
|
|
7894
|
+
prefix: params.prefix,
|
|
7895
|
+
minCount: params.minCount,
|
|
7896
|
+
entryTypes: params.entryTypes
|
|
7897
|
+
});
|
|
7898
|
+
return {
|
|
7899
|
+
content: [{
|
|
7900
|
+
type: "text",
|
|
7901
|
+
text: JSON.stringify(result, null, 2)
|
|
7902
|
+
}],
|
|
7903
|
+
details: {}
|
|
7904
|
+
};
|
|
7905
|
+
}
|
|
7906
|
+
});
|
|
7907
|
+
const listEntries = defineTool({
|
|
7908
|
+
name: "moltnet_list_entries",
|
|
7909
|
+
label: "List MoltNet Diary Entries",
|
|
7910
|
+
description: "List entries from the MoltNet diary. When `entryIds` is provided, batch-fetches those specific entries (max 50) and returns full fields including entryType, contentSignature, and contentHash for signature checks. Otherwise returns recent entries with a content preview.",
|
|
7911
|
+
parameters: Type.Object({
|
|
7912
|
+
limit: Type.Optional(Type.Number({ description: "Max entries to return (default 10)" })),
|
|
7913
|
+
tag: Type.Optional(Type.String({ description: "Filter by tag (optional)" })),
|
|
7914
|
+
entryIds: Type.Optional(Type.Array(Type.String(), {
|
|
7915
|
+
description: "Batch-fetch specific entries by UUID (max 50). Overrides `limit` and `tag` for selection.",
|
|
7916
|
+
maxItems: 50
|
|
7917
|
+
}))
|
|
7918
|
+
}),
|
|
7919
|
+
async execute(_id, params) {
|
|
7920
|
+
const { agent, diaryId } = ensureConnected(config);
|
|
7921
|
+
const query = {
|
|
7922
|
+
orderBy: "createdAt",
|
|
7923
|
+
order: "desc"
|
|
7924
|
+
};
|
|
7925
|
+
const batchMode = !!params.entryIds?.length;
|
|
7926
|
+
if (batchMode) query.ids = params.entryIds;
|
|
7927
|
+
else {
|
|
7928
|
+
query.limit = params.limit ?? 10;
|
|
7929
|
+
if (params.tag) query.tag = params.tag;
|
|
7311
7930
|
}
|
|
7931
|
+
const entries = await agent.entries.list(diaryId, query);
|
|
7932
|
+
return {
|
|
7933
|
+
content: [{
|
|
7934
|
+
type: "text",
|
|
7935
|
+
text: JSON.stringify(entries.items?.map((e) => batchMode ? {
|
|
7936
|
+
id: e.id,
|
|
7937
|
+
title: e.title,
|
|
7938
|
+
entryType: e.entryType,
|
|
7939
|
+
tags: e.tags,
|
|
7940
|
+
importance: e.importance,
|
|
7941
|
+
contentHash: e.contentHash,
|
|
7942
|
+
contentSignature: e.contentSignature,
|
|
7943
|
+
signingNonce: e.signingNonce,
|
|
7944
|
+
createdAt: e.createdAt
|
|
7945
|
+
} : {
|
|
7946
|
+
id: e.id,
|
|
7947
|
+
title: e.title,
|
|
7948
|
+
tags: e.tags,
|
|
7949
|
+
importance: e.importance,
|
|
7950
|
+
createdAt: e.createdAt,
|
|
7951
|
+
contentPreview: typeof e.content === "string" ? e.content.slice(0, 200) : void 0
|
|
7952
|
+
}), null, 2)
|
|
7953
|
+
}],
|
|
7954
|
+
details: {}
|
|
7955
|
+
};
|
|
7956
|
+
}
|
|
7957
|
+
});
|
|
7958
|
+
const getEntry = defineTool({
|
|
7959
|
+
name: "moltnet_get_entry",
|
|
7960
|
+
label: "Get MoltNet Diary Entry",
|
|
7961
|
+
description: "Get the full content of a specific diary entry by ID.",
|
|
7962
|
+
parameters: Type.Object({ entryId: Type.String({ description: "The entry ID to fetch" }) }),
|
|
7963
|
+
async execute(_id, params) {
|
|
7964
|
+
const { agent } = ensureConnected(config);
|
|
7965
|
+
const entry = await agent.entries.get(params.entryId);
|
|
7966
|
+
return {
|
|
7967
|
+
content: [{
|
|
7968
|
+
type: "text",
|
|
7969
|
+
text: JSON.stringify({
|
|
7970
|
+
id: entry.id,
|
|
7971
|
+
title: entry.title,
|
|
7972
|
+
content: entry.content,
|
|
7973
|
+
tags: entry.tags,
|
|
7974
|
+
importance: entry.importance,
|
|
7975
|
+
createdAt: entry.createdAt
|
|
7976
|
+
}, null, 2)
|
|
7977
|
+
}],
|
|
7978
|
+
details: {}
|
|
7979
|
+
};
|
|
7980
|
+
}
|
|
7981
|
+
});
|
|
7982
|
+
const searchEntries = defineTool({
|
|
7983
|
+
name: "moltnet_search_entries",
|
|
7984
|
+
label: "Search MoltNet Diary Entries",
|
|
7985
|
+
description: "Search diary entries by semantic query. Uses vector similarity to find relevant entries.",
|
|
7986
|
+
parameters: Type.Object({
|
|
7987
|
+
query: Type.String({ description: "Natural language search query" }),
|
|
7988
|
+
limit: Type.Optional(Type.Number({ description: "Max results (default 5)" }))
|
|
7312
7989
|
}),
|
|
7990
|
+
async execute(_id, params) {
|
|
7991
|
+
const { agent, diaryId } = ensureConnected(config);
|
|
7992
|
+
const results = await agent.entries.search({
|
|
7993
|
+
diaryId,
|
|
7994
|
+
query: params.query,
|
|
7995
|
+
limit: params.limit ?? 5
|
|
7996
|
+
});
|
|
7997
|
+
return {
|
|
7998
|
+
content: [{
|
|
7999
|
+
type: "text",
|
|
8000
|
+
text: JSON.stringify(results.results?.map((e) => ({
|
|
8001
|
+
id: e.id,
|
|
8002
|
+
title: e.title,
|
|
8003
|
+
tags: e.tags,
|
|
8004
|
+
importance: e.importance,
|
|
8005
|
+
contentPreview: typeof e.content === "string" ? e.content.slice(0, 200) : void 0
|
|
8006
|
+
})), null, 2)
|
|
8007
|
+
}],
|
|
8008
|
+
details: {}
|
|
8009
|
+
};
|
|
8010
|
+
}
|
|
8011
|
+
});
|
|
8012
|
+
const createEntry = defineTool({
|
|
8013
|
+
name: "moltnet_create_entry",
|
|
8014
|
+
label: "Create MoltNet Diary Entry",
|
|
8015
|
+
description: "Create a new diary entry to record decisions, findings, incidents, or reflections.",
|
|
8016
|
+
parameters: Type.Object({
|
|
8017
|
+
title: Type.String({ description: "Entry title (concise, descriptive)" }),
|
|
8018
|
+
content: Type.String({ description: "Entry content (markdown)" }),
|
|
8019
|
+
tags: Type.Optional(Type.Array(Type.String(), { description: "Tags for categorization" })),
|
|
8020
|
+
importance: Type.Optional(Type.Number({ description: "Importance 1-10 (default 5)" }))
|
|
8021
|
+
}),
|
|
8022
|
+
async execute(_id, params) {
|
|
8023
|
+
const { agent, diaryId } = ensureConnected(config);
|
|
8024
|
+
const entry = await agent.entries.create(diaryId, {
|
|
8025
|
+
title: params.title,
|
|
8026
|
+
content: params.content,
|
|
8027
|
+
tags: params.tags ?? [],
|
|
8028
|
+
importance: params.importance ?? 5
|
|
8029
|
+
});
|
|
8030
|
+
return {
|
|
8031
|
+
content: [{
|
|
8032
|
+
type: "text",
|
|
8033
|
+
text: JSON.stringify({
|
|
8034
|
+
id: entry.id,
|
|
8035
|
+
title: entry.title,
|
|
8036
|
+
createdAt: entry.createdAt
|
|
8037
|
+
}, null, 2)
|
|
8038
|
+
}],
|
|
8039
|
+
details: {}
|
|
8040
|
+
};
|
|
8041
|
+
}
|
|
8042
|
+
});
|
|
8043
|
+
const reviewSessionErrors = defineTool({
|
|
8044
|
+
name: "moltnet_review_session_errors",
|
|
8045
|
+
label: "Review Session Tool Errors",
|
|
8046
|
+
description: "Review tool failures buffered during this session (isError=true results). Use this to decide whether any failures are worth persisting as a diary entry via moltnet_create_entry. Most failures are transient (denied prompts, empty greps, mid-iteration typecheck errors) and should NOT be written to the diary — only persist incidents that represent a real finding (root cause identified, non-obvious workaround, recurring pattern). Pass clear=true to drop the buffer after reviewing.",
|
|
8047
|
+
parameters: Type.Object({ clear: Type.Optional(Type.Boolean({ description: "If true, empty the buffer after returning it. Use once you have decided whether to persist." })) }),
|
|
8048
|
+
async execute(_id, params) {
|
|
8049
|
+
const errors = config.getSessionErrors();
|
|
8050
|
+
const payload = {
|
|
8051
|
+
count: errors.length,
|
|
8052
|
+
errors: errors.map((e) => ({
|
|
8053
|
+
toolName: e.toolName,
|
|
8054
|
+
toolCallId: e.toolCallId,
|
|
8055
|
+
timestamp: new Date(e.timestamp).toISOString(),
|
|
8056
|
+
input: e.input,
|
|
8057
|
+
error: e.error
|
|
8058
|
+
}))
|
|
8059
|
+
};
|
|
8060
|
+
if (params.clear) config.clearSessionErrors();
|
|
8061
|
+
return {
|
|
8062
|
+
content: [{
|
|
8063
|
+
type: "text",
|
|
8064
|
+
text: JSON.stringify(payload, null, 2)
|
|
8065
|
+
}],
|
|
8066
|
+
details: {}
|
|
8067
|
+
};
|
|
8068
|
+
}
|
|
8069
|
+
});
|
|
8070
|
+
const HOST_EXEC_ALLOWED = new Set([
|
|
8071
|
+
"git",
|
|
8072
|
+
"gh",
|
|
8073
|
+
"moltnet"
|
|
8074
|
+
]);
|
|
8075
|
+
const hostExecBaseEnv = config.hostExecBaseEnv ?? HOST_EXEC_DEFAULT_BASE_ENV;
|
|
8076
|
+
const HOST_EXEC_TIMEOUT_MS = 6e4;
|
|
8077
|
+
return [
|
|
8078
|
+
getPack,
|
|
8079
|
+
createPack,
|
|
8080
|
+
getPackProvenance,
|
|
8081
|
+
renderPack,
|
|
8082
|
+
listRenderedPacks,
|
|
8083
|
+
getRenderedPack,
|
|
8084
|
+
verifyRenderedPack,
|
|
8085
|
+
judgeRenderedPack,
|
|
8086
|
+
diaryTags,
|
|
8087
|
+
listEntries,
|
|
8088
|
+
getEntry,
|
|
8089
|
+
searchEntries,
|
|
8090
|
+
createEntry,
|
|
8091
|
+
reviewSessionErrors,
|
|
7313
8092
|
defineTool({
|
|
7314
|
-
name: "
|
|
7315
|
-
label: "
|
|
7316
|
-
description: "
|
|
8093
|
+
name: "moltnet_host_exec",
|
|
8094
|
+
label: "Run command on host (escape hatch — requires user approval)",
|
|
8095
|
+
description: "Runs a command on the HOST machine, outside the sandbox VM. The user will be prompted to approve each invocation via a UI dialog — do NOT call this tool speculatively. Use ONLY when a sandboxed operation is impossible — e.g. `git push`, `gh pr create`.\n\nAllowed executables: git, gh, moltnet. Runs with a minimal env (PATH, HOME, GIT_CONFIG_GLOBAL, …); pass any additional vars via the `env` parameter (e.g. GH_TOKEN). Every invocation is logged as an auditable host execution.",
|
|
7317
8096
|
parameters: Type.Object({
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
8097
|
+
executable: Type.String({ description: "Executable to run (git | gh | moltnet)" }),
|
|
8098
|
+
args: Type.Array(Type.String(), { description: "Arguments to pass to the executable" }),
|
|
8099
|
+
env: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "Additional environment variables for this invocation (e.g. { \"GH_TOKEN\": \"...\" }). Merged on top of the minimal base env." }))
|
|
7321
8100
|
}),
|
|
7322
8101
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
let renderedContent;
|
|
7328
|
-
let rubric;
|
|
7329
|
-
if (params.nonce) {
|
|
7330
|
-
if (params.rubric) throw new Error("`rubric` is only supported in local mode (omit `nonce`).");
|
|
7331
|
-
const claim = await agent.packs.claimVerification(params.renderedPackId);
|
|
7332
|
-
sourceEntriesMd = buildSourceEntriesMarkdown(claim.sourceEntries);
|
|
7333
|
-
renderedContent = claim.renderedContent;
|
|
7334
|
-
rubric = claim.rubric?.trim() ? claim.rubric : DEFAULT_RUBRIC;
|
|
7335
|
-
} else {
|
|
7336
|
-
const rendered = await agent.packs.getRendered(params.renderedPackId);
|
|
7337
|
-
if (!rendered.content?.trim()) throw new Error(`rendered pack ${params.renderedPackId} has empty content`);
|
|
7338
|
-
const sourcePack = await agent.packs.get(rendered.sourcePackId, { expand: "entries" });
|
|
7339
|
-
if (!sourcePack.entries || sourcePack.entries.length === 0) throw new Error(`source pack ${rendered.sourcePackId} has no entries`);
|
|
7340
|
-
sourceEntriesMd = buildSourceEntriesMarkdown(sourcePack.entries.map((entry) => ({
|
|
7341
|
-
title: entry.entry.title,
|
|
7342
|
-
content: entry.entry.content
|
|
7343
|
-
})));
|
|
7344
|
-
renderedContent = rendered.content;
|
|
7345
|
-
rubric = params.rubric?.trim() ? params.rubric : DEFAULT_RUBRIC;
|
|
8102
|
+
if (!HOST_EXEC_ALLOWED.has(params.executable)) throw new Error(`host_exec: '${params.executable}' is not in the allowed list (${[...HOST_EXEC_ALLOWED].join(", ")}). Extend HOST_EXEC_ALLOWED only after explicit security review.`);
|
|
8103
|
+
if (ctx?.ui) {
|
|
8104
|
+
const cmdDisplay = [params.executable, ...params.args].join(" ");
|
|
8105
|
+
if (!await ctx.ui.confirm("Allow host command?", `The agent wants to run on your machine:\n\n ${cmdDisplay}\n\nAllow?`)) throw new Error(`host_exec: user declined approval for: ${cmdDisplay}`);
|
|
7346
8106
|
}
|
|
7347
|
-
|
|
8107
|
+
const cwd = config.getHostCwd?.() ?? process.cwd();
|
|
8108
|
+
const baseEnv = {};
|
|
8109
|
+
for (const key of hostExecBaseEnv) {
|
|
8110
|
+
const val = process.env[key];
|
|
8111
|
+
if (val !== void 0) baseEnv[key] = val;
|
|
8112
|
+
}
|
|
8113
|
+
const mergedEnv = {
|
|
8114
|
+
...baseEnv,
|
|
8115
|
+
...params.env ?? {}
|
|
8116
|
+
};
|
|
8117
|
+
let stdout;
|
|
8118
|
+
let stderr = "";
|
|
7348
8119
|
try {
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
8120
|
+
stdout = execFileSync(params.executable, params.args, {
|
|
8121
|
+
encoding: "utf8",
|
|
8122
|
+
cwd,
|
|
8123
|
+
env: mergedEnv,
|
|
8124
|
+
stdio: [
|
|
8125
|
+
"pipe",
|
|
8126
|
+
"pipe",
|
|
8127
|
+
"pipe"
|
|
8128
|
+
],
|
|
8129
|
+
timeout: HOST_EXEC_TIMEOUT_MS
|
|
7354
8130
|
});
|
|
7355
8131
|
} catch (err) {
|
|
7356
|
-
|
|
8132
|
+
const e = err;
|
|
8133
|
+
stdout = e.stdout ?? "";
|
|
8134
|
+
stderr = e.stderr ?? e.message ?? String(err);
|
|
7357
8135
|
}
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
}, null, 2)
|
|
7366
|
-
}],
|
|
7367
|
-
details: {}
|
|
8136
|
+
const result = {
|
|
8137
|
+
host_exec: true,
|
|
8138
|
+
executable: params.executable,
|
|
8139
|
+
args: params.args,
|
|
8140
|
+
cwd,
|
|
8141
|
+
stdout: stdout.trimEnd(),
|
|
8142
|
+
stderr: stderr.trimEnd() || void 0
|
|
7368
8143
|
};
|
|
7369
|
-
const recipe = computePiJudgeRecipeCid({
|
|
7370
|
-
judgePrompt: JUDGE_SYSTEM_PROMPT,
|
|
7371
|
-
rubric,
|
|
7372
|
-
promptAsset: JUDGE_PROMPT_ASSET_PATH,
|
|
7373
|
-
rubricAsset: RUBRIC_ASSET_PATH
|
|
7374
|
-
});
|
|
7375
|
-
const providerName = model.provider ?? "pi";
|
|
7376
|
-
const modelId = model.id ?? "unknown";
|
|
7377
|
-
const submit = await agent.packs.submitVerification(params.renderedPackId, {
|
|
7378
|
-
nonce: params.nonce,
|
|
7379
|
-
coverage: scores.coverage,
|
|
7380
|
-
grounding: scores.grounding,
|
|
7381
|
-
faithfulness: scores.faithfulness,
|
|
7382
|
-
transcript: scores.reasoning,
|
|
7383
|
-
judgeModel: modelId,
|
|
7384
|
-
judgeProvider: providerName,
|
|
7385
|
-
judgeBinaryCid: recipe.cid
|
|
7386
|
-
});
|
|
7387
|
-
return {
|
|
7388
|
-
content: [{
|
|
7389
|
-
type: "text",
|
|
7390
|
-
text: JSON.stringify({
|
|
7391
|
-
mode: "proctored",
|
|
7392
|
-
renderedPackId: params.renderedPackId,
|
|
7393
|
-
scores,
|
|
7394
|
-
submission: submit,
|
|
7395
|
-
judgeRecipeCid: recipe.cid,
|
|
7396
|
-
judgeRecipeManifest: recipe.manifest
|
|
7397
|
-
}, null, 2)
|
|
7398
|
-
}],
|
|
7399
|
-
details: {}
|
|
7400
|
-
};
|
|
7401
|
-
}
|
|
7402
|
-
}),
|
|
7403
|
-
defineTool({
|
|
7404
|
-
name: "moltnet_diary_tags",
|
|
7405
|
-
label: "List MoltNet Diary Tags",
|
|
7406
|
-
description: "Inventory tags on the current diary with entry counts. Cheap reconnaissance before committing to a search or list — use it to discover scope prefixes and cluster sizes. Optional prefix/minCount/entryTypes filters narrow the result.",
|
|
7407
|
-
parameters: Type.Object({
|
|
7408
|
-
prefix: Type.Optional(Type.String({ description: "Filter to tags starting with this prefix (e.g. \"scope:\")" })),
|
|
7409
|
-
minCount: Type.Optional(Type.Number({ description: "Exclude tags with fewer than this many entries" })),
|
|
7410
|
-
entryTypes: Type.Optional(Type.Array(Type.Union([
|
|
7411
|
-
Type.Literal("episodic"),
|
|
7412
|
-
Type.Literal("semantic"),
|
|
7413
|
-
Type.Literal("procedural"),
|
|
7414
|
-
Type.Literal("reflection"),
|
|
7415
|
-
Type.Literal("identity"),
|
|
7416
|
-
Type.Literal("soul")
|
|
7417
|
-
]), { description: "Scope the tag count to these entry types" }))
|
|
7418
|
-
}),
|
|
7419
|
-
async execute(_id, params) {
|
|
7420
|
-
const { agent, diaryId } = ensureConnected(config);
|
|
7421
|
-
const result = await agent.diaries.tags(diaryId, {
|
|
7422
|
-
prefix: params.prefix,
|
|
7423
|
-
minCount: params.minCount,
|
|
7424
|
-
entryTypes: params.entryTypes
|
|
7425
|
-
});
|
|
7426
8144
|
return {
|
|
7427
8145
|
content: [{
|
|
7428
8146
|
type: "text",
|
|
@@ -7431,169 +8149,6 @@ function createMoltNetTools(config) {
|
|
|
7431
8149
|
details: {}
|
|
7432
8150
|
};
|
|
7433
8151
|
}
|
|
7434
|
-
}),
|
|
7435
|
-
defineTool({
|
|
7436
|
-
name: "moltnet_list_entries",
|
|
7437
|
-
label: "List MoltNet Diary Entries",
|
|
7438
|
-
description: "List entries from the MoltNet diary. When `entryIds` is provided, batch-fetches those specific entries (max 50) and returns full fields including entryType, contentSignature, and contentHash for signature checks. Otherwise returns recent entries with a content preview.",
|
|
7439
|
-
parameters: Type.Object({
|
|
7440
|
-
limit: Type.Optional(Type.Number({ description: "Max entries to return (default 10)" })),
|
|
7441
|
-
tag: Type.Optional(Type.String({ description: "Filter by tag (optional)" })),
|
|
7442
|
-
entryIds: Type.Optional(Type.Array(Type.String(), {
|
|
7443
|
-
description: "Batch-fetch specific entries by UUID (max 50). Overrides `limit` and `tag` for selection.",
|
|
7444
|
-
maxItems: 50
|
|
7445
|
-
}))
|
|
7446
|
-
}),
|
|
7447
|
-
async execute(_id, params) {
|
|
7448
|
-
const { agent, diaryId } = ensureConnected(config);
|
|
7449
|
-
const query = {
|
|
7450
|
-
orderBy: "createdAt",
|
|
7451
|
-
order: "desc"
|
|
7452
|
-
};
|
|
7453
|
-
const batchMode = !!params.entryIds?.length;
|
|
7454
|
-
if (batchMode) query.ids = params.entryIds;
|
|
7455
|
-
else {
|
|
7456
|
-
query.limit = params.limit ?? 10;
|
|
7457
|
-
if (params.tag) query.tag = params.tag;
|
|
7458
|
-
}
|
|
7459
|
-
const entries = await agent.entries.list(diaryId, query);
|
|
7460
|
-
return {
|
|
7461
|
-
content: [{
|
|
7462
|
-
type: "text",
|
|
7463
|
-
text: JSON.stringify(entries.items?.map((e) => batchMode ? {
|
|
7464
|
-
id: e.id,
|
|
7465
|
-
title: e.title,
|
|
7466
|
-
entryType: e.entryType,
|
|
7467
|
-
tags: e.tags,
|
|
7468
|
-
importance: e.importance,
|
|
7469
|
-
contentHash: e.contentHash,
|
|
7470
|
-
contentSignature: e.contentSignature,
|
|
7471
|
-
signingNonce: e.signingNonce,
|
|
7472
|
-
createdAt: e.createdAt
|
|
7473
|
-
} : {
|
|
7474
|
-
id: e.id,
|
|
7475
|
-
title: e.title,
|
|
7476
|
-
tags: e.tags,
|
|
7477
|
-
importance: e.importance,
|
|
7478
|
-
createdAt: e.createdAt,
|
|
7479
|
-
contentPreview: typeof e.content === "string" ? e.content.slice(0, 200) : void 0
|
|
7480
|
-
}), null, 2)
|
|
7481
|
-
}],
|
|
7482
|
-
details: {}
|
|
7483
|
-
};
|
|
7484
|
-
}
|
|
7485
|
-
}),
|
|
7486
|
-
defineTool({
|
|
7487
|
-
name: "moltnet_get_entry",
|
|
7488
|
-
label: "Get MoltNet Diary Entry",
|
|
7489
|
-
description: "Get the full content of a specific diary entry by ID.",
|
|
7490
|
-
parameters: Type.Object({ entryId: Type.String({ description: "The entry ID to fetch" }) }),
|
|
7491
|
-
async execute(_id, params) {
|
|
7492
|
-
const { agent } = ensureConnected(config);
|
|
7493
|
-
const entry = await agent.entries.get(params.entryId);
|
|
7494
|
-
return {
|
|
7495
|
-
content: [{
|
|
7496
|
-
type: "text",
|
|
7497
|
-
text: JSON.stringify({
|
|
7498
|
-
id: entry.id,
|
|
7499
|
-
title: entry.title,
|
|
7500
|
-
content: entry.content,
|
|
7501
|
-
tags: entry.tags,
|
|
7502
|
-
importance: entry.importance,
|
|
7503
|
-
createdAt: entry.createdAt
|
|
7504
|
-
}, null, 2)
|
|
7505
|
-
}],
|
|
7506
|
-
details: {}
|
|
7507
|
-
};
|
|
7508
|
-
}
|
|
7509
|
-
}),
|
|
7510
|
-
defineTool({
|
|
7511
|
-
name: "moltnet_search_entries",
|
|
7512
|
-
label: "Search MoltNet Diary Entries",
|
|
7513
|
-
description: "Search diary entries by semantic query. Uses vector similarity to find relevant entries.",
|
|
7514
|
-
parameters: Type.Object({
|
|
7515
|
-
query: Type.String({ description: "Natural language search query" }),
|
|
7516
|
-
limit: Type.Optional(Type.Number({ description: "Max results (default 5)" }))
|
|
7517
|
-
}),
|
|
7518
|
-
async execute(_id, params) {
|
|
7519
|
-
const { agent, diaryId } = ensureConnected(config);
|
|
7520
|
-
const results = await agent.entries.search({
|
|
7521
|
-
diaryId,
|
|
7522
|
-
query: params.query,
|
|
7523
|
-
limit: params.limit ?? 5
|
|
7524
|
-
});
|
|
7525
|
-
return {
|
|
7526
|
-
content: [{
|
|
7527
|
-
type: "text",
|
|
7528
|
-
text: JSON.stringify(results.results?.map((e) => ({
|
|
7529
|
-
id: e.id,
|
|
7530
|
-
title: e.title,
|
|
7531
|
-
tags: e.tags,
|
|
7532
|
-
importance: e.importance,
|
|
7533
|
-
contentPreview: typeof e.content === "string" ? e.content.slice(0, 200) : void 0
|
|
7534
|
-
})), null, 2)
|
|
7535
|
-
}],
|
|
7536
|
-
details: {}
|
|
7537
|
-
};
|
|
7538
|
-
}
|
|
7539
|
-
}),
|
|
7540
|
-
defineTool({
|
|
7541
|
-
name: "moltnet_create_entry",
|
|
7542
|
-
label: "Create MoltNet Diary Entry",
|
|
7543
|
-
description: "Create a new diary entry to record decisions, findings, incidents, or reflections.",
|
|
7544
|
-
parameters: Type.Object({
|
|
7545
|
-
title: Type.String({ description: "Entry title (concise, descriptive)" }),
|
|
7546
|
-
content: Type.String({ description: "Entry content (markdown)" }),
|
|
7547
|
-
tags: Type.Optional(Type.Array(Type.String(), { description: "Tags for categorization" })),
|
|
7548
|
-
importance: Type.Optional(Type.Number({ description: "Importance 1-10 (default 5)" }))
|
|
7549
|
-
}),
|
|
7550
|
-
async execute(_id, params) {
|
|
7551
|
-
const { agent, diaryId } = ensureConnected(config);
|
|
7552
|
-
const entry = await agent.entries.create(diaryId, {
|
|
7553
|
-
title: params.title,
|
|
7554
|
-
content: params.content,
|
|
7555
|
-
tags: params.tags ?? [],
|
|
7556
|
-
importance: params.importance ?? 5
|
|
7557
|
-
});
|
|
7558
|
-
return {
|
|
7559
|
-
content: [{
|
|
7560
|
-
type: "text",
|
|
7561
|
-
text: JSON.stringify({
|
|
7562
|
-
id: entry.id,
|
|
7563
|
-
title: entry.title,
|
|
7564
|
-
createdAt: entry.createdAt
|
|
7565
|
-
}, null, 2)
|
|
7566
|
-
}],
|
|
7567
|
-
details: {}
|
|
7568
|
-
};
|
|
7569
|
-
}
|
|
7570
|
-
}),
|
|
7571
|
-
defineTool({
|
|
7572
|
-
name: "moltnet_review_session_errors",
|
|
7573
|
-
label: "Review Session Tool Errors",
|
|
7574
|
-
description: "Review tool failures buffered during this session (isError=true results). Use this to decide whether any failures are worth persisting as a diary entry via moltnet_create_entry. Most failures are transient (denied prompts, empty greps, mid-iteration typecheck errors) and should NOT be written to the diary — only persist incidents that represent a real finding (root cause identified, non-obvious workaround, recurring pattern). Pass clear=true to drop the buffer after reviewing.",
|
|
7575
|
-
parameters: Type.Object({ clear: Type.Optional(Type.Boolean({ description: "If true, empty the buffer after returning it. Use once you have decided whether to persist." })) }),
|
|
7576
|
-
async execute(_id, params) {
|
|
7577
|
-
const errors = config.getSessionErrors();
|
|
7578
|
-
const payload = {
|
|
7579
|
-
count: errors.length,
|
|
7580
|
-
errors: errors.map((e) => ({
|
|
7581
|
-
toolName: e.toolName,
|
|
7582
|
-
toolCallId: e.toolCallId,
|
|
7583
|
-
timestamp: new Date(e.timestamp).toISOString(),
|
|
7584
|
-
input: e.input,
|
|
7585
|
-
error: e.error
|
|
7586
|
-
}))
|
|
7587
|
-
};
|
|
7588
|
-
if (params.clear) config.clearSessionErrors();
|
|
7589
|
-
return {
|
|
7590
|
-
content: [{
|
|
7591
|
-
type: "text",
|
|
7592
|
-
text: JSON.stringify(payload, null, 2)
|
|
7593
|
-
}],
|
|
7594
|
-
details: {}
|
|
7595
|
-
};
|
|
7596
|
-
}
|
|
7597
8152
|
})
|
|
7598
8153
|
];
|
|
7599
8154
|
}
|
|
@@ -7929,10 +8484,6 @@ function createGondolinBashOps(vm, localCwd) {
|
|
|
7929
8484
|
}
|
|
7930
8485
|
//#endregion
|
|
7931
8486
|
//#region src/vm-manager.ts
|
|
7932
|
-
/**
|
|
7933
|
-
* VM lifecycle manager: resume checkpoint, inject credentials, configure
|
|
7934
|
-
* egress, fix TLS, and provide clean shutdown.
|
|
7935
|
-
*/
|
|
7936
8487
|
var GUEST_WORKSPACE$1 = "/workspace";
|
|
7937
8488
|
/**
|
|
7938
8489
|
* Resolve the main worktree root (where .moltnet/ lives — it's untracked,
|
|
@@ -7963,6 +8514,15 @@ function loadCredentials(agentDir) {
|
|
|
7963
8514
|
const sshPrivateKey = existsSync(path.join(sshDir, "id_ed25519")) ? readFileSync(path.join(sshDir, "id_ed25519"), "utf8") : null;
|
|
7964
8515
|
const sshPublicKey = existsSync(path.join(sshDir, "id_ed25519.pub")) ? readFileSync(path.join(sshDir, "id_ed25519.pub"), "utf8") : null;
|
|
7965
8516
|
const allowedSigners = existsSync(path.join(sshDir, "allowed_signers")) ? readFileSync(path.join(sshDir, "allowed_signers"), "utf8") : null;
|
|
8517
|
+
let githubAppPem = null;
|
|
8518
|
+
let githubAppPemFilename = null;
|
|
8519
|
+
const pemPath = JSON.parse(moltnetJson).github?.private_key_path;
|
|
8520
|
+
if (pemPath) if (!existsSync(pemPath)) process.stderr.write(`[pi-extension] Warning: github.private_key_path not found at ${pemPath} — moltnet github token will fail inside the guest
|
|
8521
|
+
`);
|
|
8522
|
+
else {
|
|
8523
|
+
githubAppPem = readFileSync(pemPath, "utf8");
|
|
8524
|
+
githubAppPemFilename = path.basename(pemPath);
|
|
8525
|
+
}
|
|
7966
8526
|
return {
|
|
7967
8527
|
moltnetJson,
|
|
7968
8528
|
agentEnvRaw,
|
|
@@ -7971,7 +8531,9 @@ function loadCredentials(agentDir) {
|
|
|
7971
8531
|
gitconfig,
|
|
7972
8532
|
sshPrivateKey,
|
|
7973
8533
|
sshPublicKey,
|
|
7974
|
-
allowedSigners
|
|
8534
|
+
allowedSigners,
|
|
8535
|
+
githubAppPem,
|
|
8536
|
+
githubAppPemFilename
|
|
7975
8537
|
};
|
|
7976
8538
|
}
|
|
7977
8539
|
/**
|
|
@@ -8018,13 +8580,15 @@ async function resumeVm(config) {
|
|
|
8018
8580
|
apiHost,
|
|
8019
8581
|
...config.extraAllowedHosts ?? []
|
|
8020
8582
|
] });
|
|
8583
|
+
const vmAgentDir = `/home/agent/.moltnet/${config.agentName}`;
|
|
8021
8584
|
const vmAgentEnv = {};
|
|
8022
8585
|
for (const [k, v] of Object.entries(creds.agentEnv)) {
|
|
8023
8586
|
if (v === void 0 || v === "") continue;
|
|
8024
|
-
if (k === "GIT_CONFIG_GLOBAL") vmAgentEnv[k] =
|
|
8025
|
-
else if (k.endsWith("_PRIVATE_KEY_PATH")) vmAgentEnv[k] =
|
|
8587
|
+
if (k === "GIT_CONFIG_GLOBAL") vmAgentEnv[k] = `${vmAgentDir}/gitconfig`;
|
|
8588
|
+
else if (k.endsWith("_PRIVATE_KEY_PATH")) vmAgentEnv[k] = `${vmAgentDir}/${path.basename(v)}`;
|
|
8026
8589
|
else vmAgentEnv[k] = v;
|
|
8027
8590
|
}
|
|
8591
|
+
vmAgentEnv.MOLTNET_CREDENTIALS_PATH = `${vmAgentDir}/moltnet.json`;
|
|
8028
8592
|
const vfsConfig = config.sandboxConfig?.vfs;
|
|
8029
8593
|
let workspaceProvider = new RealFSProvider(config.mountPath);
|
|
8030
8594
|
if (vfsConfig?.shadow?.length) {
|
|
@@ -8059,11 +8623,11 @@ async function resumeVm(config) {
|
|
|
8059
8623
|
'`);
|
|
8060
8624
|
await vm.exec(`sh -c 'echo "nameserver 8.8.8.8
|
|
8061
8625
|
nameserver 1.1.1.1" > /etc/resolv.conf'`);
|
|
8062
|
-
const vmAgentDir = `/home/agent/.moltnet/${config.agentName}`;
|
|
8063
8626
|
const vmSshDir = `${vmAgentDir}/ssh`;
|
|
8064
8627
|
await vm.exec(`mkdir -p ${vmAgentDir}/ssh /home/agent/.pi/agent`);
|
|
8065
8628
|
await vm.fs.writeFile("/home/agent/.pi/agent/auth.json", creds.piAuthJson, { mode: 384 });
|
|
8066
|
-
|
|
8629
|
+
const vmMoltnetJson = rewriteMoltnetJsonPaths(creds.moltnetJson, vmAgentDir, vmSshDir, creds.githubAppPemFilename);
|
|
8630
|
+
await vm.fs.writeFile(`${vmAgentDir}/moltnet.json`, vmMoltnetJson, { mode: 384 });
|
|
8067
8631
|
await vm.fs.writeFile(`${vmAgentDir}/env`, creds.agentEnvRaw, { mode: 384 });
|
|
8068
8632
|
if (creds.gitconfig) {
|
|
8069
8633
|
const vmSigningKey = `${vmSshDir}/id_ed25519`;
|
|
@@ -8074,6 +8638,7 @@ nameserver 1.1.1.1" > /etc/resolv.conf'`);
|
|
|
8074
8638
|
if (creds.sshPrivateKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519`, creds.sshPrivateKey, { mode: 384 });
|
|
8075
8639
|
if (creds.sshPublicKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519.pub`, creds.sshPublicKey, { mode: 420 });
|
|
8076
8640
|
if (creds.allowedSigners) await vm.fs.writeFile(`${vmSshDir}/allowed_signers`, creds.allowedSigners, { mode: 420 });
|
|
8641
|
+
if (creds.githubAppPem && creds.githubAppPemFilename) await vm.fs.writeFile(`${vmAgentDir}/${creds.githubAppPemFilename}`, creds.githubAppPem, { mode: 384 });
|
|
8077
8642
|
await vm.exec("chown -R agent:agent /home/agent/.pi /home/agent/.moltnet");
|
|
8078
8643
|
return {
|
|
8079
8644
|
vm,
|
|
@@ -8084,6 +8649,43 @@ nameserver 1.1.1.1" > /etc/resolv.conf'`);
|
|
|
8084
8649
|
};
|
|
8085
8650
|
}
|
|
8086
8651
|
/**
|
|
8652
|
+
* Rewrite host-absolute paths inside moltnet.json to VM-local equivalents.
|
|
8653
|
+
*
|
|
8654
|
+
* Fields rewritten:
|
|
8655
|
+
* ssh.private_key_path → <vmSshDir>/<basename of original>
|
|
8656
|
+
* ssh.public_key_path → <vmSshDir>/<basename of original>
|
|
8657
|
+
* git.config_path → <vmAgentDir>/gitconfig
|
|
8658
|
+
* github.private_key_path → <vmAgentDir>/<pemFilename> (if present)
|
|
8659
|
+
*
|
|
8660
|
+
* All other fields are passed through unchanged.
|
|
8661
|
+
* Throws if moltnetJson is not valid JSON — callers must not inject a broken
|
|
8662
|
+
* moltnet.json into the guest.
|
|
8663
|
+
*/
|
|
8664
|
+
function rewriteMoltnetJsonPaths(moltnetJson, vmAgentDir, vmSshDir, githubAppPemFilename) {
|
|
8665
|
+
const config = JSON.parse(moltnetJson);
|
|
8666
|
+
if (config.ssh && typeof config.ssh === "object") {
|
|
8667
|
+
const ssh = config.ssh;
|
|
8668
|
+
const origPrivate = typeof ssh.private_key_path === "string" ? ssh.private_key_path : null;
|
|
8669
|
+
const origPublic = typeof ssh.public_key_path === "string" ? ssh.public_key_path : null;
|
|
8670
|
+
config.ssh = {
|
|
8671
|
+
...ssh,
|
|
8672
|
+
...origPrivate !== null && { private_key_path: `${vmSshDir}/${path.basename(origPrivate)}` },
|
|
8673
|
+
...origPublic !== null && { public_key_path: `${vmSshDir}/${path.basename(origPublic)}` }
|
|
8674
|
+
};
|
|
8675
|
+
}
|
|
8676
|
+
if (config.git && typeof config.git === "object") {
|
|
8677
|
+
const git = { ...config.git };
|
|
8678
|
+
git.config_path = `${vmAgentDir}/gitconfig`;
|
|
8679
|
+
config.git = git;
|
|
8680
|
+
}
|
|
8681
|
+
if (githubAppPemFilename && config.github && typeof config.github === "object") {
|
|
8682
|
+
const github = { ...config.github };
|
|
8683
|
+
github.private_key_path = `${vmAgentDir}/${githubAppPemFilename}`;
|
|
8684
|
+
config.github = github;
|
|
8685
|
+
}
|
|
8686
|
+
return JSON.stringify(config);
|
|
8687
|
+
}
|
|
8688
|
+
/**
|
|
8087
8689
|
* Ensure `[worktree] useRelativePaths = true` is set in the given
|
|
8088
8690
|
* gitconfig text. If the section exists, rewrite the key; otherwise
|
|
8089
8691
|
* append a new section.
|
|
@@ -8121,9 +8723,9 @@ if (!FormatRegistry.Has("date-time")) FormatRegistry.Set("date-time", (v) => !Nu
|
|
|
8121
8723
|
* own signed rows and CIDv1 lookup. The schema below is designed to
|
|
8122
8724
|
* carry forward unchanged — only storage and addressing differ.
|
|
8123
8725
|
*
|
|
8124
|
-
* Until Phase 2 lands, `
|
|
8726
|
+
* Until Phase 2 lands, `rubricId` + `version` + `contentHash` are
|
|
8125
8727
|
* informational fields the author fills in; no uniqueness is enforced.
|
|
8126
|
-
* `
|
|
8728
|
+
* `contentHash` is optional in Phase 1 because the *task*'s input_cid
|
|
8127
8729
|
* is the authoritative commitment.
|
|
8128
8730
|
*/
|
|
8129
8731
|
/**
|
|
@@ -8159,12 +8761,12 @@ var RubricCriterion = Type$1.Object({
|
|
|
8159
8761
|
* (stored row `body`); only the addressing mechanism differs.
|
|
8160
8762
|
*/
|
|
8161
8763
|
var Rubric = Type$1.Object({
|
|
8162
|
-
|
|
8764
|
+
rubricId: Type$1.String({ minLength: 1 }),
|
|
8163
8765
|
version: Type$1.String({ minLength: 1 }),
|
|
8164
8766
|
preamble: Type$1.Optional(Type$1.String()),
|
|
8165
8767
|
criteria: Type$1.Array(RubricCriterion, { minItems: 1 }),
|
|
8166
8768
|
scope: Type$1.Optional(Type$1.String()),
|
|
8167
|
-
|
|
8769
|
+
contentHash: Type$1.Optional(Type$1.String())
|
|
8168
8770
|
}, {
|
|
8169
8771
|
$id: "Rubric",
|
|
8170
8772
|
additionalProperties: false
|
|
@@ -8220,25 +8822,25 @@ var AssessBriefCriterion = Type$1.Object({
|
|
|
8220
8822
|
additionalProperties: false
|
|
8221
8823
|
});
|
|
8222
8824
|
var AssessBriefInput = Type$1.Object({
|
|
8223
|
-
|
|
8825
|
+
targetTaskId: Type$1.String({ format: "uuid" }),
|
|
8224
8826
|
criteria: Type$1.Array(AssessBriefCriterion, { minItems: 1 }),
|
|
8225
|
-
|
|
8827
|
+
rubricPreamble: Type$1.Optional(Type$1.String())
|
|
8226
8828
|
}, {
|
|
8227
8829
|
$id: "AssessBriefInput",
|
|
8228
8830
|
additionalProperties: false
|
|
8229
8831
|
});
|
|
8230
8832
|
/** One score line. */
|
|
8231
8833
|
var AssessBriefScore = Type$1.Object({
|
|
8232
|
-
|
|
8834
|
+
criterionId: Type$1.String({ minLength: 1 }),
|
|
8233
8835
|
score: Type$1.Number({
|
|
8234
8836
|
minimum: 0,
|
|
8235
8837
|
maximum: 1
|
|
8236
8838
|
}),
|
|
8237
8839
|
rationale: Type$1.Optional(Type$1.String()),
|
|
8238
8840
|
evidence: Type$1.Optional(Type$1.Object({
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8841
|
+
commitsVerified: Type$1.Number(),
|
|
8842
|
+
commitsTotal: Type$1.Number(),
|
|
8843
|
+
signatureFailures: Type$1.Array(Type$1.String())
|
|
8242
8844
|
}, { additionalProperties: false }))
|
|
8243
8845
|
}, {
|
|
8244
8846
|
$id: "AssessBriefScore",
|
|
@@ -8251,7 +8853,7 @@ var AssessBriefOutput = Type$1.Object({
|
|
|
8251
8853
|
maximum: 1
|
|
8252
8854
|
}),
|
|
8253
8855
|
verdict: Type$1.String({ minLength: 1 }),
|
|
8254
|
-
|
|
8856
|
+
judgeModel: Type$1.Optional(Type$1.String())
|
|
8255
8857
|
}, {
|
|
8256
8858
|
$id: "AssessBriefOutput",
|
|
8257
8859
|
additionalProperties: false
|
|
@@ -8282,15 +8884,15 @@ var EntryTypeFilter = Type$1.Union([
|
|
|
8282
8884
|
Type$1.Literal("reflection")
|
|
8283
8885
|
]);
|
|
8284
8886
|
var CuratePackInput = Type$1.Object({
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8887
|
+
diaryId: Type$1.String({ format: "uuid" }),
|
|
8888
|
+
taskPrompt: Type$1.String({ minLength: 1 }),
|
|
8889
|
+
entryTypes: Type$1.Optional(Type$1.Array(EntryTypeFilter, { minItems: 1 })),
|
|
8890
|
+
tagFilters: Type$1.Optional(Type$1.Object({
|
|
8289
8891
|
include: Type$1.Optional(Type$1.Array(Type$1.String())),
|
|
8290
8892
|
exclude: Type$1.Optional(Type$1.Array(Type$1.String())),
|
|
8291
8893
|
prefix: Type$1.Optional(Type$1.String())
|
|
8292
8894
|
}, { additionalProperties: false })),
|
|
8293
|
-
|
|
8895
|
+
tokenBudget: Type$1.Optional(Type$1.Number({ minimum: 500 })),
|
|
8294
8896
|
recipe: Type$1.Optional(Type$1.Union([Type$1.Literal("topic-focused-v1"), Type$1.Literal("scope-inventory-v1")]))
|
|
8295
8897
|
}, {
|
|
8296
8898
|
$id: "CuratePackInput",
|
|
@@ -8302,18 +8904,18 @@ var CuratePackInput = Type$1.Object({
|
|
|
8302
8904
|
* is the receipt.
|
|
8303
8905
|
*/
|
|
8304
8906
|
var CuratePackOutput = Type$1.Object({
|
|
8305
|
-
|
|
8306
|
-
|
|
8907
|
+
packId: Type$1.String({ format: "uuid" }),
|
|
8908
|
+
packCid: Type$1.String({ minLength: 1 }),
|
|
8307
8909
|
entries: Type$1.Array(Type$1.Object({
|
|
8308
|
-
|
|
8910
|
+
entryId: Type$1.String({ format: "uuid" }),
|
|
8309
8911
|
rank: Type$1.Number({ minimum: 1 }),
|
|
8310
8912
|
rationale: Type$1.String({ minLength: 1 })
|
|
8311
8913
|
}, { additionalProperties: false }), { minItems: 1 }),
|
|
8312
|
-
|
|
8914
|
+
recipeParams: Type$1.Record(Type$1.String(), Type$1.Unknown()),
|
|
8313
8915
|
checkpoints: Type$1.Optional(Type$1.Array(Type$1.Object({
|
|
8314
8916
|
phase: Type$1.String({ minLength: 1 }),
|
|
8315
|
-
|
|
8316
|
-
|
|
8917
|
+
candidateIds: Type$1.Array(Type$1.String({ format: "uuid" })),
|
|
8918
|
+
droppedIds: Type$1.Optional(Type$1.Array(Type$1.String({ format: "uuid" }))),
|
|
8317
8919
|
notes: Type$1.String({ minLength: 1 })
|
|
8318
8920
|
}, { additionalProperties: false }))),
|
|
8319
8921
|
summary: Type$1.String({ minLength: 1 })
|
|
@@ -8334,9 +8936,9 @@ var FULFILL_BRIEF_TYPE = "fulfill_brief";
|
|
|
8334
8936
|
var FulfillBriefInput = Type$1.Object({
|
|
8335
8937
|
brief: Type$1.String({ minLength: 1 }),
|
|
8336
8938
|
title: Type$1.Optional(Type$1.String()),
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8939
|
+
acceptanceCriteria: Type$1.Optional(Type$1.Array(Type$1.String())),
|
|
8940
|
+
seedFiles: Type$1.Optional(Type$1.Array(Type$1.String())),
|
|
8941
|
+
scopeHint: Type$1.Optional(Type$1.String())
|
|
8340
8942
|
}, {
|
|
8341
8943
|
$id: "FulfillBriefInput",
|
|
8342
8944
|
additionalProperties: false
|
|
@@ -8350,10 +8952,10 @@ var FulfillBriefOutput = Type$1.Object({
|
|
|
8350
8952
|
commits: Type$1.Array(Type$1.Object({
|
|
8351
8953
|
sha: Type$1.String({ minLength: 7 }),
|
|
8352
8954
|
message: Type$1.String(),
|
|
8353
|
-
|
|
8955
|
+
diaryEntryId: Type$1.Union([Type$1.String({ format: "uuid" }), Type$1.Null()])
|
|
8354
8956
|
}, { additionalProperties: false })),
|
|
8355
|
-
|
|
8356
|
-
|
|
8957
|
+
pullRequestUrl: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
8958
|
+
diaryEntryIds: Type$1.Array(Type$1.String({ format: "uuid" })),
|
|
8357
8959
|
summary: Type$1.String({ minLength: 1 })
|
|
8358
8960
|
}, {
|
|
8359
8961
|
$id: "FulfillBriefOutput",
|
|
@@ -8384,8 +8986,8 @@ var FulfillBriefOutput = Type$1.Object({
|
|
|
8384
8986
|
*/
|
|
8385
8987
|
var JUDGE_PACK_TYPE = "judge_pack";
|
|
8386
8988
|
var JudgePackInput = Type$1.Object({
|
|
8387
|
-
|
|
8388
|
-
|
|
8989
|
+
renderedPackId: Type$1.String({ format: "uuid" }),
|
|
8990
|
+
sourcePackId: Type$1.String({ format: "uuid" }),
|
|
8389
8991
|
rubric: Rubric
|
|
8390
8992
|
}, {
|
|
8391
8993
|
$id: "JudgePackInput",
|
|
@@ -8393,7 +8995,7 @@ var JudgePackInput = Type$1.Object({
|
|
|
8393
8995
|
});
|
|
8394
8996
|
/** One scored criterion. Mirrors `AssessBriefScore`. */
|
|
8395
8997
|
var JudgePackScore = Type$1.Object({
|
|
8396
|
-
|
|
8998
|
+
criterionId: Type$1.String({ minLength: 1 }),
|
|
8397
8999
|
score: Type$1.Number({
|
|
8398
9000
|
minimum: 0,
|
|
8399
9001
|
maximum: 1
|
|
@@ -8411,8 +9013,8 @@ var JudgePackOutput = Type$1.Object({
|
|
|
8411
9013
|
maximum: 1
|
|
8412
9014
|
}),
|
|
8413
9015
|
verdict: Type$1.String({ minLength: 1 }),
|
|
8414
|
-
|
|
8415
|
-
|
|
9016
|
+
judgeModel: Type$1.Optional(Type$1.String()),
|
|
9017
|
+
rendererBinaryCid: Type$1.Optional(Type$1.Union([Type$1.String(), Type$1.Null()]))
|
|
8416
9018
|
}, {
|
|
8417
9019
|
$id: "JudgePackOutput",
|
|
8418
9020
|
additionalProperties: false
|
|
@@ -8436,7 +9038,7 @@ var JudgePackOutput = Type$1.Object({
|
|
|
8436
9038
|
*/
|
|
8437
9039
|
var RENDER_PACK_TYPE = "render_pack";
|
|
8438
9040
|
var RenderPackInput = Type$1.Object({
|
|
8439
|
-
|
|
9041
|
+
packId: Type$1.String({ format: "uuid" }),
|
|
8440
9042
|
persist: Type$1.Optional(Type$1.Boolean()),
|
|
8441
9043
|
pinned: Type$1.Optional(Type$1.Boolean())
|
|
8442
9044
|
}, {
|
|
@@ -8444,11 +9046,11 @@ var RenderPackInput = Type$1.Object({
|
|
|
8444
9046
|
additionalProperties: false
|
|
8445
9047
|
});
|
|
8446
9048
|
var RenderPackOutput = Type$1.Object({
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
9049
|
+
renderedPackId: Type$1.Union([Type$1.String({ format: "uuid" }), Type$1.Null()]),
|
|
9050
|
+
renderedCid: Type$1.String({ minLength: 1 }),
|
|
9051
|
+
renderMethod: Type$1.String({ minLength: 1 }),
|
|
9052
|
+
byteSize: Type$1.Number({ minimum: 0 }),
|
|
9053
|
+
entriesRendered: Type$1.Number({ minimum: 0 }),
|
|
8452
9054
|
summary: Type$1.String({ minLength: 1 })
|
|
8453
9055
|
}, {
|
|
8454
9056
|
$id: "RenderPackOutput",
|
|
@@ -8509,6 +9111,41 @@ var BUILT_IN_TASK_TYPES = {
|
|
|
8509
9111
|
}
|
|
8510
9112
|
};
|
|
8511
9113
|
//#endregion
|
|
9114
|
+
//#region ../tasks/src/task-type-registry.ts
|
|
9115
|
+
var schemaCids = null;
|
|
9116
|
+
function getTaskTypeRegistry() {
|
|
9117
|
+
if (!schemaCids) throw new Error("Task type registry not initialized. Call initTaskTypeRegistry() first.");
|
|
9118
|
+
return schemaCids;
|
|
9119
|
+
}
|
|
9120
|
+
new Proxy({}, { get(_, prop) {
|
|
9121
|
+
if (typeof prop !== "string") return void 0;
|
|
9122
|
+
return getTaskTypeRegistry().get(prop);
|
|
9123
|
+
} });
|
|
9124
|
+
//#endregion
|
|
9125
|
+
//#region ../tasks/src/validation.ts
|
|
9126
|
+
function getTaskTypeEntry(taskType) {
|
|
9127
|
+
const taskTypes = BUILT_IN_TASK_TYPES;
|
|
9128
|
+
if (!Object.prototype.hasOwnProperty.call(taskTypes, taskType)) return;
|
|
9129
|
+
return taskTypes[taskType];
|
|
9130
|
+
}
|
|
9131
|
+
function formatField(prefix, path) {
|
|
9132
|
+
return path ? `${prefix}${path}` : prefix;
|
|
9133
|
+
}
|
|
9134
|
+
function schemaErrors(prefix, schema, value) {
|
|
9135
|
+
return [...Value.Errors(schema, value)].map((error) => ({
|
|
9136
|
+
field: formatField(prefix, error.path),
|
|
9137
|
+
message: error.message
|
|
9138
|
+
}));
|
|
9139
|
+
}
|
|
9140
|
+
function validateTaskOutput(taskType, output) {
|
|
9141
|
+
const entry = getTaskTypeEntry(taskType);
|
|
9142
|
+
if (!entry) return [{
|
|
9143
|
+
field: "taskType",
|
|
9144
|
+
message: `Unknown task type: ${taskType}`
|
|
9145
|
+
}];
|
|
9146
|
+
return schemaErrors("output", entry.outputSchema, output);
|
|
9147
|
+
}
|
|
9148
|
+
//#endregion
|
|
8512
9149
|
//#region ../tasks/src/wire.ts
|
|
8513
9150
|
/**
|
|
8514
9151
|
* Wire-format types for the MoltNet Task model.
|
|
@@ -8520,7 +9157,7 @@ var BUILT_IN_TASK_TYPES = {
|
|
|
8520
9157
|
* - `TaskReporter` output records (PR 0)
|
|
8521
9158
|
*
|
|
8522
9159
|
* Invariant: every property on `Task` is type-neutral (applies to all
|
|
8523
|
-
* `
|
|
9160
|
+
* `taskType`s). Type-specific payloads live inside `input` / `output`
|
|
8524
9161
|
* JSONB, validated against schemas registered under `task_types`.
|
|
8525
9162
|
*
|
|
8526
9163
|
* Identity rule:
|
|
@@ -8563,8 +9200,8 @@ var IsoTimestamp = Type$1.String({ format: "date-time" });
|
|
|
8563
9200
|
* Embedded in `tasks.references` JSONB array.
|
|
8564
9201
|
*/
|
|
8565
9202
|
var TaskRef = Type$1.Object({
|
|
8566
|
-
|
|
8567
|
-
|
|
9203
|
+
taskId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9204
|
+
outputCid: Cid,
|
|
8568
9205
|
role: Type$1.Union([
|
|
8569
9206
|
Type$1.Literal("judged_work"),
|
|
8570
9207
|
Type$1.Literal("reviewed_diff"),
|
|
@@ -8593,11 +9230,11 @@ var TaskRef = Type$1.Object({
|
|
|
8593
9230
|
* `TaskOutput.usage` for convenience.
|
|
8594
9231
|
*/
|
|
8595
9232
|
var TaskUsage = Type$1.Object({
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
9233
|
+
inputTokens: Type$1.Integer({ minimum: 0 }),
|
|
9234
|
+
outputTokens: Type$1.Integer({ minimum: 0 }),
|
|
9235
|
+
cacheReadTokens: Type$1.Optional(Type$1.Integer({ minimum: 0 })),
|
|
9236
|
+
cacheWriteTokens: Type$1.Optional(Type$1.Integer({ minimum: 0 })),
|
|
9237
|
+
toolCalls: Type$1.Optional(Type$1.Integer({ minimum: 0 })),
|
|
8601
9238
|
model: Type$1.Optional(Type$1.String()),
|
|
8602
9239
|
provider: Type$1.Optional(Type$1.String())
|
|
8603
9240
|
}, {
|
|
@@ -8617,62 +9254,65 @@ var TaskError = Type$1.Object({
|
|
|
8617
9254
|
additionalProperties: false
|
|
8618
9255
|
});
|
|
8619
9256
|
Type$1.Object({
|
|
8620
|
-
|
|
8621
|
-
|
|
9257
|
+
agentId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9258
|
+
humanId: Type$1.Union([Uuid, Type$1.Null()])
|
|
8622
9259
|
}, {
|
|
8623
9260
|
$id: "ActorPair",
|
|
8624
9261
|
additionalProperties: false
|
|
8625
9262
|
});
|
|
8626
9263
|
Type$1.Object({
|
|
8627
9264
|
id: Uuid,
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
9265
|
+
taskType: Type$1.String({ minLength: 1 }),
|
|
9266
|
+
teamId: Uuid,
|
|
9267
|
+
diaryId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9268
|
+
outputKind: OutputKind,
|
|
8632
9269
|
input: Type$1.Record(Type$1.String(), Type$1.Unknown()),
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
9270
|
+
inputSchemaCid: Cid,
|
|
9271
|
+
inputCid: Cid,
|
|
9272
|
+
criteriaCid: Type$1.Union([Cid, Type$1.Null()]),
|
|
8636
9273
|
references: Type$1.Array(TaskRef),
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
9274
|
+
correlationId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9275
|
+
imposedByAgentId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9276
|
+
imposedByHumanId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9277
|
+
acceptedAttemptN: Type$1.Union([Type$1.Number(), Type$1.Null()]),
|
|
8641
9278
|
status: TaskStatus,
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
9279
|
+
queuedAt: IsoTimestamp,
|
|
9280
|
+
completedAt: Type$1.Union([IsoTimestamp, Type$1.Null()]),
|
|
9281
|
+
expiresAt: Type$1.Union([IsoTimestamp, Type$1.Null()]),
|
|
9282
|
+
cancelledByAgentId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9283
|
+
cancelledByHumanId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9284
|
+
cancelReason: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
9285
|
+
maxAttempts: Type$1.Number({ minimum: 1 })
|
|
8649
9286
|
}, {
|
|
8650
9287
|
$id: "Task",
|
|
8651
9288
|
additionalProperties: false
|
|
8652
9289
|
});
|
|
8653
9290
|
Type$1.Object({
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
9291
|
+
taskId: Uuid,
|
|
9292
|
+
attemptN: Type$1.Number({ minimum: 1 }),
|
|
9293
|
+
claimedByAgentId: Uuid,
|
|
9294
|
+
runtimeId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9295
|
+
claimedAt: IsoTimestamp,
|
|
9296
|
+
startedAt: Type$1.Union([IsoTimestamp, Type$1.Null()]),
|
|
9297
|
+
completedAt: Type$1.Union([IsoTimestamp, Type$1.Null()]),
|
|
8661
9298
|
status: TaskAttemptStatus,
|
|
8662
9299
|
output: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
8663
|
-
|
|
9300
|
+
outputCid: Type$1.Union([Cid, Type$1.Null()]),
|
|
8664
9301
|
error: Type$1.Union([TaskError, Type$1.Null()]),
|
|
8665
9302
|
usage: Type$1.Union([TaskUsage, Type$1.Null()]),
|
|
8666
|
-
|
|
8667
|
-
|
|
9303
|
+
contentSignature: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
9304
|
+
signedAt: Type$1.Union([IsoTimestamp, Type$1.Null()])
|
|
8668
9305
|
}, {
|
|
8669
9306
|
$id: "TaskAttempt",
|
|
8670
9307
|
additionalProperties: false
|
|
8671
9308
|
});
|
|
8672
9309
|
Type$1.Object({
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
seq: Type$1.Number({
|
|
9310
|
+
taskId: Uuid,
|
|
9311
|
+
attemptN: Type$1.Number({ minimum: 1 }),
|
|
9312
|
+
seq: Type$1.Number({
|
|
9313
|
+
minimum: 0,
|
|
9314
|
+
description: "Monotonically increasing integer assigned by the server. Use as the afterSeq cursor on the list-messages endpoint to poll for new messages without re-fetching earlier ones."
|
|
9315
|
+
}),
|
|
8676
9316
|
timestamp: IsoTimestamp,
|
|
8677
9317
|
kind: TaskMessageKind,
|
|
8678
9318
|
payload: Type$1.Record(Type$1.String(), Type$1.Unknown())
|
|
@@ -8681,34 +9321,34 @@ Type$1.Object({
|
|
|
8681
9321
|
additionalProperties: false
|
|
8682
9322
|
});
|
|
8683
9323
|
Type$1.Object({
|
|
8684
|
-
|
|
8685
|
-
|
|
9324
|
+
taskId: Uuid,
|
|
9325
|
+
attemptN: Type$1.Number({ minimum: 1 }),
|
|
8686
9326
|
status: Type$1.Union([
|
|
8687
9327
|
Type$1.Literal("completed"),
|
|
8688
9328
|
Type$1.Literal("failed"),
|
|
8689
9329
|
Type$1.Literal("cancelled")
|
|
8690
9330
|
]),
|
|
8691
9331
|
output: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
8692
|
-
|
|
9332
|
+
outputCid: Type$1.Union([Cid, Type$1.Null()]),
|
|
8693
9333
|
usage: TaskUsage,
|
|
8694
|
-
|
|
9334
|
+
durationMs: Type$1.Number({ minimum: 0 }),
|
|
8695
9335
|
error: Type$1.Optional(TaskError),
|
|
8696
|
-
|
|
9336
|
+
contentSignature: Type$1.Optional(Type$1.String())
|
|
8697
9337
|
}, {
|
|
8698
9338
|
$id: "TaskOutput",
|
|
8699
9339
|
additionalProperties: false
|
|
8700
9340
|
});
|
|
8701
9341
|
Type$1.Object({
|
|
8702
|
-
|
|
8703
|
-
|
|
9342
|
+
runtimeId: Uuid,
|
|
9343
|
+
agentId: Uuid,
|
|
8704
9344
|
timestamp: IsoTimestamp,
|
|
8705
9345
|
status: Type$1.Union([
|
|
8706
9346
|
Type$1.Literal("idle"),
|
|
8707
9347
|
Type$1.Literal("busy"),
|
|
8708
9348
|
Type$1.Literal("draining")
|
|
8709
9349
|
]),
|
|
8710
|
-
|
|
8711
|
-
|
|
9350
|
+
activeTaskIds: Type$1.Array(Uuid),
|
|
9351
|
+
supportedTaskTypes: Type$1.Array(Type$1.String())
|
|
8712
9352
|
}, {
|
|
8713
9353
|
$id: "RuntimeHeartbeat",
|
|
8714
9354
|
additionalProperties: false
|
|
@@ -8729,10 +9369,10 @@ function buildAssessBriefPrompt(input, ctx) {
|
|
|
8729
9369
|
...ctx.target.diaryEntryIds.map((id) => `- ${id}`),
|
|
8730
9370
|
""
|
|
8731
9371
|
].join("\n") : "";
|
|
8732
|
-
const preambleSection = input.
|
|
9372
|
+
const preambleSection = input.rubricPreamble ? [
|
|
8733
9373
|
"### Rubric preamble",
|
|
8734
9374
|
"",
|
|
8735
|
-
input.
|
|
9375
|
+
input.rubricPreamble,
|
|
8736
9376
|
""
|
|
8737
9377
|
].join("\n") : "";
|
|
8738
9378
|
return [
|
|
@@ -8763,12 +9403,12 @@ function buildAssessBriefPrompt(input, ctx) {
|
|
|
8763
9403
|
"",
|
|
8764
9404
|
"- `llm_judged`: score 0..1 continuous. `rationale` REQUIRED (2–4 sentences).",
|
|
8765
9405
|
"- `boolean`: score exactly 0 or 1. `rationale` optional.",
|
|
8766
|
-
"- `deterministic_signature_check`: run `moltnet entry verify` on every diary entry listed above AND `git verify-commit` on every commit. Score 1 iff ALL signatures are valid; otherwise 0. Populate `evidence.
|
|
9406
|
+
"- `deterministic_signature_check`: run `moltnet entry verify` on every diary entry listed above AND `git verify-commit` on every commit. Score 1 iff ALL signatures are valid; otherwise 0. Populate `evidence.commitsVerified`, `evidence.commitsTotal`, `evidence.signatureFailures`.",
|
|
8767
9407
|
"",
|
|
8768
9408
|
"### Final output",
|
|
8769
9409
|
"",
|
|
8770
9410
|
"Emit a JSON object matching `AssessBriefOutput`:",
|
|
8771
|
-
" { \"scores\": [{
|
|
9411
|
+
" { \"scores\": [{criterionId, score, rationale?, evidence?}], \"composite\", \"verdict\", \"judgeModel\"? }",
|
|
8772
9412
|
"`composite` = Σ(weight_i × score_i) recomputed. The runtime will reject a mismatch.",
|
|
8773
9413
|
"Write a signed diary entry (tags: \"judgment\", \"assess_brief\") capturing the rationale before emitting the JSON."
|
|
8774
9414
|
].filter(Boolean).join("\n");
|
|
@@ -8790,7 +9430,7 @@ function buildAssessBriefPrompt(input, ctx) {
|
|
|
8790
9430
|
* N isolated `createAgentSession` children (one per tag cluster or
|
|
8791
9431
|
* entry_type axis the curator picks after recon), each with a narrow
|
|
8792
9432
|
* tool subset and a turn cap, and returns compressed summaries. Parent
|
|
8793
|
-
* curator keeps a warm context and only sees {
|
|
9433
|
+
* curator keeps a warm context and only sees {candidateIds, notes}
|
|
8794
9434
|
* per probe — mirrors the fan-out pattern pi-mono SDK example #13
|
|
8795
9435
|
* (session runtime) + #05 (custom tools) makes possible. Until that
|
|
8796
9436
|
* lands, the `checkpoints[]` output field is the fallback: curator
|
|
@@ -8798,7 +9438,7 @@ function buildAssessBriefPrompt(input, ctx) {
|
|
|
8798
9438
|
* resume without replaying the tool history.
|
|
8799
9439
|
*/
|
|
8800
9440
|
function buildCuratePackPrompt(input, ctx) {
|
|
8801
|
-
const {
|
|
9441
|
+
const { diaryId, taskPrompt, entryTypes, tagFilters, tokenBudget, recipe } = input;
|
|
8802
9442
|
const effectiveEntryTypes = entryTypes ?? [
|
|
8803
9443
|
"semantic",
|
|
8804
9444
|
"episodic",
|
|
@@ -8885,7 +9525,7 @@ function buildCuratePackPrompt(input, ctx) {
|
|
|
8885
9525
|
"",
|
|
8886
9526
|
"The tool returns a JSON payload whose top-level fields are `packId` and",
|
|
8887
9527
|
"`packCid` (NOT `id`). Copy those exact UUID/CID strings verbatim into",
|
|
8888
|
-
"`
|
|
9528
|
+
"`packId` and `packCid` in your final output — do not substitute an",
|
|
8889
9529
|
"entry id, do not reformat, do not fabricate a UUID.",
|
|
8890
9530
|
"",
|
|
8891
9531
|
"## Hard constraints",
|
|
@@ -8902,14 +9542,14 @@ function buildCuratePackPrompt(input, ctx) {
|
|
|
8902
9542
|
"Write to stdout a JSON object matching `CuratePackOutput`:",
|
|
8903
9543
|
"```",
|
|
8904
9544
|
"{",
|
|
8905
|
-
" \"
|
|
8906
|
-
" \"
|
|
9545
|
+
" \"packId\": \"<uuid>\",",
|
|
9546
|
+
" \"packCid\": \"<cid>\",",
|
|
8907
9547
|
" \"entries\": [",
|
|
8908
|
-
" { \"
|
|
9548
|
+
" { \"entryId\": \"<uuid>\", \"rank\": 1, \"rationale\": \"<why>\" }",
|
|
8909
9549
|
" ],",
|
|
8910
|
-
" \"
|
|
9550
|
+
" \"recipeParams\": { \"recipe\": \"...\", \"prompt\": \"...\", ... },",
|
|
8911
9551
|
" \"checkpoints\": [",
|
|
8912
|
-
" { \"phase\": \"recon\", \"
|
|
9552
|
+
" { \"phase\": \"recon\", \"candidateIds\": [...], \"droppedIds\": [...], \"notes\": \"...\" }",
|
|
8913
9553
|
" ],",
|
|
8914
9554
|
" \"summary\": \"<2-4 sentences: what you looked for, how you narrowed, what defines the final set>\"",
|
|
8915
9555
|
"}",
|
|
@@ -8927,7 +9567,7 @@ function buildCuratePackPrompt(input, ctx) {
|
|
|
8927
9567
|
* is told to inspect them itself.
|
|
8928
9568
|
*/
|
|
8929
9569
|
function buildFulfillBriefPrompt(input, ctx) {
|
|
8930
|
-
const { brief, title,
|
|
9570
|
+
const { brief, title, acceptanceCriteria, seedFiles, scopeHint } = input;
|
|
8931
9571
|
const criteriaSection = acceptanceCriteria?.length ? [
|
|
8932
9572
|
"### Acceptance criteria",
|
|
8933
9573
|
"",
|
|
@@ -8976,14 +9616,14 @@ function buildFulfillBriefPrompt(input, ctx) {
|
|
|
8976
9616
|
"### Final output",
|
|
8977
9617
|
"",
|
|
8978
9618
|
"When done, write to stdout a JSON object with shape matching `FulfillBriefOutput`:",
|
|
8979
|
-
" { \"branch\", \"commits\": [{sha, message,
|
|
9619
|
+
" { \"branch\", \"commits\": [{sha, message, diaryEntryId}], \"pullRequestUrl\", \"diaryEntryIds\", \"summary\" }",
|
|
8980
9620
|
"The runtime parses this as the structured task output. Failing to emit it is a failure."
|
|
8981
9621
|
].filter(Boolean).join("\n");
|
|
8982
9622
|
}
|
|
8983
9623
|
//#endregion
|
|
8984
9624
|
//#region ../agent-runtime/src/prompts/judge-pack.ts
|
|
8985
9625
|
function buildJudgePackPrompt(input, ctx) {
|
|
8986
|
-
const {
|
|
9626
|
+
const { renderedPackId, sourcePackId, rubric } = input;
|
|
8987
9627
|
const criteriaList = rubric.criteria.map((c, i) => `${i + 1}. **${c.id}** (weight ${c.weight}, scoring: \`${c.scoring}\`) — ${c.description}`).join("\n");
|
|
8988
9628
|
const preambleSection = rubric.preamble ? [
|
|
8989
9629
|
"### Rubric preamble",
|
|
@@ -9006,7 +9646,7 @@ function buildJudgePackPrompt(input, ctx) {
|
|
|
9006
9646
|
"",
|
|
9007
9647
|
`- **Rendered pack**: \`${renderedPackId}\``,
|
|
9008
9648
|
`- **Source pack**: \`${sourcePackId}\``,
|
|
9009
|
-
`- **Rubric**: \`${rubric.
|
|
9649
|
+
`- **Rubric**: \`${rubric.rubricId}\` v${rubric.version}`,
|
|
9010
9650
|
"",
|
|
9011
9651
|
preambleSection,
|
|
9012
9652
|
"## Workflow",
|
|
@@ -9045,12 +9685,12 @@ function buildJudgePackPrompt(input, ctx) {
|
|
|
9045
9685
|
" non-empty `contentSignature`. If `required_signed_total` is 0,",
|
|
9046
9686
|
" score = 1. Populate `evidence` with `{ entries_verified,",
|
|
9047
9687
|
" entries_total, required_signed_total, required_signed_ok,",
|
|
9048
|
-
"
|
|
9688
|
+
" signatureFailures: [entryIds] }` where `signatureFailures` lists",
|
|
9049
9689
|
" ONLY the REQUIRED-SIGNED entries that lack a signature.",
|
|
9050
9690
|
"- `deterministic_coverage_check`: for every source entry, check",
|
|
9051
|
-
" whether its `
|
|
9691
|
+
" whether its `entryId` (or a stable reference like title + CID",
|
|
9052
9692
|
" prefix) appears in the rendered `content`. Score 1 iff coverage is",
|
|
9053
|
-
" complete; otherwise 0. Populate `evidence` with `{ covered, total, missing: [
|
|
9693
|
+
" complete; otherwise 0. Populate `evidence` with `{ covered, total, missing: [entryIds] }`.",
|
|
9054
9694
|
"",
|
|
9055
9695
|
"## Constraints",
|
|
9056
9696
|
"",
|
|
@@ -9064,17 +9704,17 @@ function buildJudgePackPrompt(input, ctx) {
|
|
|
9064
9704
|
"Write to stdout a JSON object matching `JudgePackOutput`:",
|
|
9065
9705
|
"```",
|
|
9066
9706
|
"{",
|
|
9067
|
-
" \"scores\": [{\"
|
|
9707
|
+
" \"scores\": [{\"criterionId\": \"...\", \"score\": 0.0, \"rationale\": \"...\", \"evidence\": {...}}],",
|
|
9068
9708
|
" \"composite\": <sum-of-weighted-scores>,",
|
|
9069
9709
|
" \"verdict\": \"<1-3 sentence overall>\",",
|
|
9070
|
-
" \"
|
|
9071
|
-
" \"
|
|
9710
|
+
" \"judgeModel\": \"<provider:model>\",",
|
|
9711
|
+
" \"rendererBinaryCid\": \"<cid-string-only-if-available>\"",
|
|
9072
9712
|
"}",
|
|
9073
9713
|
"```",
|
|
9074
|
-
"Omit `
|
|
9714
|
+
"Omit `rendererBinaryCid` entirely when no binary CID is exposed by",
|
|
9075
9715
|
"`moltnet_rendered_pack_get`. Do NOT emit `null` — the field is optional",
|
|
9076
9716
|
"and absence is the correct representation when unavailable.",
|
|
9077
|
-
`Write a signed diary entry (tags: \`judgment\`, \`judge_pack\`, \`rubric:${rubric.
|
|
9717
|
+
`Write a signed diary entry (tags: \`judgment\`, \`judge_pack\`, \`rubric:${rubric.rubricId}\`) capturing the rationale before`,
|
|
9078
9718
|
"emitting the JSON."
|
|
9079
9719
|
].filter((l) => l !== null).join("\n");
|
|
9080
9720
|
}
|
|
@@ -9085,7 +9725,7 @@ function buildJudgePackPrompt(input, ctx) {
|
|
|
9085
9725
|
* wraps `moltnet_pack_render` and emits the receipt.
|
|
9086
9726
|
*/
|
|
9087
9727
|
function buildRenderPackPrompt(input, ctx) {
|
|
9088
|
-
const {
|
|
9728
|
+
const { packId, persist = true, pinned = false } = input;
|
|
9089
9729
|
return [
|
|
9090
9730
|
"# Render Pack Agent",
|
|
9091
9731
|
"",
|
|
@@ -9125,11 +9765,11 @@ function buildRenderPackPrompt(input, ctx) {
|
|
|
9125
9765
|
"Write to stdout a JSON object matching `RenderPackOutput`:",
|
|
9126
9766
|
"```",
|
|
9127
9767
|
"{",
|
|
9128
|
-
" \"
|
|
9129
|
-
" \"
|
|
9130
|
-
" \"
|
|
9131
|
-
" \"
|
|
9132
|
-
" \"
|
|
9768
|
+
" \"renderedPackId\": \"<uuid-or-null>\",",
|
|
9769
|
+
" \"renderedCid\": \"<cid>\",",
|
|
9770
|
+
" \"renderMethod\": \"<label>\",",
|
|
9771
|
+
" \"byteSize\": <int>,",
|
|
9772
|
+
" \"entriesRendered\": <int>,",
|
|
9133
9773
|
" \"summary\": \"<1-3 sentence recap>\"",
|
|
9134
9774
|
"}",
|
|
9135
9775
|
"```",
|
|
@@ -9139,11 +9779,11 @@ function buildRenderPackPrompt(input, ctx) {
|
|
|
9139
9779
|
//#endregion
|
|
9140
9780
|
//#region ../agent-runtime/src/prompts/index.ts
|
|
9141
9781
|
/**
|
|
9142
|
-
* Resolve the correct prompt builder for `task.
|
|
9782
|
+
* Resolve the correct prompt builder for `task.taskType` and invoke it.
|
|
9143
9783
|
* Throws if the type is unknown or the input fails TypeBox validation.
|
|
9144
9784
|
*/
|
|
9145
9785
|
function buildPromptForTask(task, ctx) {
|
|
9146
|
-
switch (task.
|
|
9786
|
+
switch (task.taskType) {
|
|
9147
9787
|
case FULFILL_BRIEF_TYPE:
|
|
9148
9788
|
if (!Value.Check(FulfillBriefInput, task.input)) {
|
|
9149
9789
|
const errors = [...Value.Errors(FulfillBriefInput, task.input)];
|
|
@@ -9193,10 +9833,102 @@ function buildPromptForTask(task, ctx) {
|
|
|
9193
9833
|
diaryId: ctx.diaryId,
|
|
9194
9834
|
taskId: ctx.taskId
|
|
9195
9835
|
});
|
|
9196
|
-
default: throw new Error(`No prompt builder registered for
|
|
9836
|
+
default: throw new Error(`No prompt builder registered for taskType="${task.taskType}"`);
|
|
9197
9837
|
}
|
|
9198
9838
|
}
|
|
9199
9839
|
//#endregion
|
|
9840
|
+
//#region src/runtime/task-output.ts
|
|
9841
|
+
async function parseStructuredTaskOutput(assistantText, taskType) {
|
|
9842
|
+
const extracted = extractJsonObject(assistantText);
|
|
9843
|
+
if (!extracted) return {
|
|
9844
|
+
output: null,
|
|
9845
|
+
outputCid: null,
|
|
9846
|
+
error: {
|
|
9847
|
+
code: "output_missing",
|
|
9848
|
+
message: "Agent did not emit a parseable JSON object as its final message."
|
|
9849
|
+
}
|
|
9850
|
+
};
|
|
9851
|
+
const errors = validateTaskOutput(taskType, extracted);
|
|
9852
|
+
if (errors.length > 0) {
|
|
9853
|
+
const details = errors.slice(0, 3).map((error) => `${error.field}: ${error.message}`);
|
|
9854
|
+
const [firstError] = errors;
|
|
9855
|
+
return {
|
|
9856
|
+
output: null,
|
|
9857
|
+
outputCid: null,
|
|
9858
|
+
error: {
|
|
9859
|
+
code: firstError?.field === "taskType" ? "unknown_task_type" : "output_validation_failed",
|
|
9860
|
+
message: `Output failed schema validation: ${details.join("; ")}`
|
|
9861
|
+
}
|
|
9862
|
+
};
|
|
9863
|
+
}
|
|
9864
|
+
try {
|
|
9865
|
+
return {
|
|
9866
|
+
output: extracted,
|
|
9867
|
+
outputCid: await computeJsonCid(extracted),
|
|
9868
|
+
error: null
|
|
9869
|
+
};
|
|
9870
|
+
} catch (error) {
|
|
9871
|
+
return {
|
|
9872
|
+
output: null,
|
|
9873
|
+
outputCid: null,
|
|
9874
|
+
error: {
|
|
9875
|
+
code: "output_cid_compute_failed",
|
|
9876
|
+
message: `Validated output could not be canonicalized: ${error instanceof Error ? error.message : String(error)}`
|
|
9877
|
+
}
|
|
9878
|
+
};
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9881
|
+
/**
|
|
9882
|
+
* Find the last balanced top-level JSON object in `text` and parse it.
|
|
9883
|
+
* Tolerates markdown fences and leading prose. Returns null if parsing fails.
|
|
9884
|
+
*/
|
|
9885
|
+
function extractJsonObject(text) {
|
|
9886
|
+
if (!text) return null;
|
|
9887
|
+
const fenceMatch = /```(?:json)?\s*([\s\S]*?)```/gi;
|
|
9888
|
+
const candidates = [];
|
|
9889
|
+
for (const m of text.matchAll(fenceMatch)) candidates.push(m[1]);
|
|
9890
|
+
const scanForObject = (s) => {
|
|
9891
|
+
let depth = 0;
|
|
9892
|
+
let start = -1;
|
|
9893
|
+
let lastComplete = null;
|
|
9894
|
+
let inString = false;
|
|
9895
|
+
let escape = false;
|
|
9896
|
+
for (let i = 0; i < s.length; i++) {
|
|
9897
|
+
const ch = s[i];
|
|
9898
|
+
if (inString) {
|
|
9899
|
+
if (escape) escape = false;
|
|
9900
|
+
else if (ch === "\\") escape = true;
|
|
9901
|
+
else if (ch === "\"") inString = false;
|
|
9902
|
+
continue;
|
|
9903
|
+
}
|
|
9904
|
+
if (ch === "\"") {
|
|
9905
|
+
inString = true;
|
|
9906
|
+
continue;
|
|
9907
|
+
}
|
|
9908
|
+
if (ch === "{") {
|
|
9909
|
+
if (depth === 0) start = i;
|
|
9910
|
+
depth++;
|
|
9911
|
+
} else if (ch === "}") {
|
|
9912
|
+
depth--;
|
|
9913
|
+
if (depth === 0 && start !== -1) {
|
|
9914
|
+
lastComplete = s.slice(start, i + 1);
|
|
9915
|
+
start = -1;
|
|
9916
|
+
}
|
|
9917
|
+
}
|
|
9918
|
+
}
|
|
9919
|
+
return lastComplete;
|
|
9920
|
+
};
|
|
9921
|
+
candidates.push(text);
|
|
9922
|
+
for (let i = candidates.length - 1; i >= 0; i--) {
|
|
9923
|
+
const obj = scanForObject(candidates[i]);
|
|
9924
|
+
if (!obj) continue;
|
|
9925
|
+
try {
|
|
9926
|
+
return JSON.parse(obj);
|
|
9927
|
+
} catch {}
|
|
9928
|
+
}
|
|
9929
|
+
return null;
|
|
9930
|
+
}
|
|
9931
|
+
//#endregion
|
|
9200
9932
|
//#region src/runtime/execute-pi-task.ts
|
|
9201
9933
|
/**
|
|
9202
9934
|
* executePiTask — run a single Task attempt using pi-coding-agent inside a
|
|
@@ -9221,14 +9953,14 @@ function buildPromptForTask(task, ctx) {
|
|
|
9221
9953
|
*/
|
|
9222
9954
|
function createPiTaskExecutor(opts) {
|
|
9223
9955
|
let cachedCheckpoint = opts.checkpointPath ?? null;
|
|
9224
|
-
return async (
|
|
9956
|
+
return async (claimedTask, reporter) => {
|
|
9225
9957
|
if (!cachedCheckpoint) cachedCheckpoint = await ensureSnapshot({
|
|
9226
9958
|
config: opts.sandboxConfig?.snapshot,
|
|
9227
9959
|
onProgress: opts.onSnapshotProgress ?? ((m) => {
|
|
9228
9960
|
process.stderr.write(`[snapshot] ${m}\n`);
|
|
9229
9961
|
})
|
|
9230
9962
|
});
|
|
9231
|
-
return executePiTask(
|
|
9963
|
+
return executePiTask(claimedTask, reporter, {
|
|
9232
9964
|
...opts,
|
|
9233
9965
|
checkpointPath: cachedCheckpoint
|
|
9234
9966
|
});
|
|
@@ -9240,8 +9972,9 @@ function createPiTaskExecutor(opts) {
|
|
|
9240
9972
|
* a `TaskOutput` (failures surface as `status: 'failed'`); throws only on
|
|
9241
9973
|
* unrecoverable setup errors.
|
|
9242
9974
|
*/
|
|
9243
|
-
async function executePiTask(
|
|
9244
|
-
const
|
|
9975
|
+
async function executePiTask(claimedTask, reporter, opts) {
|
|
9976
|
+
const task = claimedTask.task;
|
|
9977
|
+
const attemptN = claimedTask.attemptN;
|
|
9245
9978
|
const startTime = Date.now();
|
|
9246
9979
|
const mountPath = opts.mountPath ?? process.cwd();
|
|
9247
9980
|
const checkpointPath = opts.checkpointPath ?? await ensureSnapshot({
|
|
@@ -9267,17 +10000,18 @@ async function executePiTask(task, reporter, opts) {
|
|
|
9267
10000
|
extraAllowedHosts: opts.extraAllowedHosts,
|
|
9268
10001
|
sandboxConfig: opts.sandboxConfig
|
|
9269
10002
|
});
|
|
9270
|
-
const diaryId = task.
|
|
10003
|
+
const diaryId = task.diaryId ?? "";
|
|
9271
10004
|
let reporterOpen = false;
|
|
9272
10005
|
let session = null;
|
|
9273
|
-
const
|
|
9274
|
-
|
|
9275
|
-
|
|
10006
|
+
const finalUsage = emptyUsage(opts.provider, opts.model);
|
|
10007
|
+
const makeFailedOutput = (code, message, usage = finalUsage) => ({
|
|
10008
|
+
taskId: task.id,
|
|
10009
|
+
attemptN,
|
|
9276
10010
|
status: "failed",
|
|
9277
10011
|
output: null,
|
|
9278
|
-
|
|
10012
|
+
outputCid: null,
|
|
9279
10013
|
usage,
|
|
9280
|
-
|
|
10014
|
+
durationMs: Date.now() - startTime,
|
|
9281
10015
|
error: {
|
|
9282
10016
|
code,
|
|
9283
10017
|
message,
|
|
@@ -9298,8 +10032,8 @@ async function executePiTask(task, reporter, opts) {
|
|
|
9298
10032
|
});
|
|
9299
10033
|
await emit("info", {
|
|
9300
10034
|
event: "execute_start",
|
|
9301
|
-
|
|
9302
|
-
|
|
10035
|
+
taskType: task.taskType,
|
|
10036
|
+
teamId: task.teamId,
|
|
9303
10037
|
provider: opts.provider,
|
|
9304
10038
|
model: opts.model
|
|
9305
10039
|
});
|
|
@@ -9330,7 +10064,9 @@ async function executePiTask(task, reporter, opts) {
|
|
|
9330
10064
|
getAgent: () => moltnetAgent,
|
|
9331
10065
|
getDiaryId: () => diaryId,
|
|
9332
10066
|
getSessionErrors: () => [],
|
|
9333
|
-
clearSessionErrors: () => {}
|
|
10067
|
+
clearSessionErrors: () => {},
|
|
10068
|
+
getHostCwd: () => mountPath,
|
|
10069
|
+
hostExecBaseEnv: new Set([...HOST_EXEC_DEFAULT_BASE_ENV, ...Object.keys(managed.credentials.agentEnv)])
|
|
9334
10070
|
});
|
|
9335
10071
|
const piAuthDir = join(homedir(), ".pi", "agent");
|
|
9336
10072
|
const modelHandle = getModel(opts.provider, opts.model);
|
|
@@ -9358,7 +10094,7 @@ async function executePiTask(task, reporter, opts) {
|
|
|
9358
10094
|
let llmAbort = false;
|
|
9359
10095
|
let assistantText = "";
|
|
9360
10096
|
let reporterError = null;
|
|
9361
|
-
const usage =
|
|
10097
|
+
const usage = finalUsage;
|
|
9362
10098
|
const recordingPromise = [];
|
|
9363
10099
|
const track = (p) => {
|
|
9364
10100
|
recordingPromise.push(p.catch((err) => {
|
|
@@ -9388,12 +10124,12 @@ async function executePiTask(task, reporter, opts) {
|
|
|
9388
10124
|
else if (event.type === "turn_end") {
|
|
9389
10125
|
const msg = event.message;
|
|
9390
10126
|
if (msg?.role === "assistant" && msg.usage) {
|
|
9391
|
-
usage.
|
|
9392
|
-
usage.
|
|
10127
|
+
usage.inputTokens += Math.max(0, msg.usage.input ?? 0);
|
|
10128
|
+
usage.outputTokens += Math.max(0, msg.usage.output ?? 0);
|
|
9393
10129
|
const cr = Math.max(0, msg.usage.cacheRead ?? 0);
|
|
9394
10130
|
const cw = Math.max(0, msg.usage.cacheWrite ?? 0);
|
|
9395
|
-
if (cr) usage.
|
|
9396
|
-
if (cw) usage.
|
|
10131
|
+
if (cr) usage.cacheReadTokens = (usage.cacheReadTokens ?? 0) + cr;
|
|
10132
|
+
if (cw) usage.cacheWriteTokens = (usage.cacheWriteTokens ?? 0) + cw;
|
|
9397
10133
|
}
|
|
9398
10134
|
track(emit("turn_end", { stop_reason: msg?.stopReason ?? "end_turn" }));
|
|
9399
10135
|
llmAbort = msg?.stopReason === "error";
|
|
@@ -9415,28 +10151,13 @@ async function executePiTask(task, reporter, opts) {
|
|
|
9415
10151
|
}
|
|
9416
10152
|
await Promise.all(recordingPromise);
|
|
9417
10153
|
let parsedOutput = null;
|
|
10154
|
+
let parsedOutputCid = null;
|
|
9418
10155
|
let parseError = null;
|
|
9419
10156
|
if (!runError && !llmAbort) {
|
|
9420
|
-
const
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
};
|
|
9425
|
-
else {
|
|
9426
|
-
const entry = BUILT_IN_TASK_TYPES[task.task_type];
|
|
9427
|
-
if (!entry) parseError = {
|
|
9428
|
-
code: "unknown_task_type",
|
|
9429
|
-
message: `No output schema registered for task_type=${task.task_type}`
|
|
9430
|
-
};
|
|
9431
|
-
else {
|
|
9432
|
-
const check = TypeCompiler.Compile(entry.outputSchema);
|
|
9433
|
-
if (check.Check(extracted)) parsedOutput = extracted;
|
|
9434
|
-
else parseError = {
|
|
9435
|
-
code: "output_validation_failed",
|
|
9436
|
-
message: `Output failed schema validation: ${[...check.Errors(extracted)].slice(0, 3).map((e) => `${e.path}: ${e.message}`).join("; ")}`
|
|
9437
|
-
};
|
|
9438
|
-
}
|
|
9439
|
-
}
|
|
10157
|
+
const parsed = await parseStructuredTaskOutput(assistantText, task.taskType);
|
|
10158
|
+
parsedOutput = parsed.output;
|
|
10159
|
+
parsedOutputCid = parsed.outputCid;
|
|
10160
|
+
parseError = parsed.error;
|
|
9440
10161
|
if (parseError) await emit("error", {
|
|
9441
10162
|
message: parseError.message,
|
|
9442
10163
|
phase: "output_validation"
|
|
@@ -9446,13 +10167,13 @@ async function executePiTask(task, reporter, opts) {
|
|
|
9446
10167
|
const errorCode = runError?.code ?? parseError?.code ?? reporterError?.code ?? (llmAbort ? "llm_api_error" : void 0);
|
|
9447
10168
|
const errorMessage = runError?.message ?? parseError?.message ?? reporterError?.message ?? (llmAbort ? "LLM API error during turn" : void 0);
|
|
9448
10169
|
return {
|
|
9449
|
-
|
|
9450
|
-
|
|
10170
|
+
taskId: task.id,
|
|
10171
|
+
attemptN,
|
|
9451
10172
|
status,
|
|
9452
10173
|
output: parsedOutput,
|
|
9453
|
-
|
|
10174
|
+
outputCid: parsedOutputCid,
|
|
9454
10175
|
usage,
|
|
9455
|
-
|
|
10176
|
+
durationMs: Date.now() - startTime,
|
|
9456
10177
|
...errorCode && errorMessage ? { error: {
|
|
9457
10178
|
code: errorCode,
|
|
9458
10179
|
message: errorMessage,
|
|
@@ -9467,7 +10188,7 @@ async function executePiTask(task, reporter, opts) {
|
|
|
9467
10188
|
} catch {}
|
|
9468
10189
|
if (reporterOpen) {
|
|
9469
10190
|
try {
|
|
9470
|
-
await reporter.finalize(
|
|
10191
|
+
await reporter.finalize(finalUsage);
|
|
9471
10192
|
} catch {}
|
|
9472
10193
|
try {
|
|
9473
10194
|
await reporter.close();
|
|
@@ -9478,8 +10199,8 @@ async function executePiTask(task, reporter, opts) {
|
|
|
9478
10199
|
}
|
|
9479
10200
|
function emptyUsage(provider, model) {
|
|
9480
10201
|
return {
|
|
9481
|
-
|
|
9482
|
-
|
|
10202
|
+
inputTokens: 0,
|
|
10203
|
+
outputTokens: 0,
|
|
9483
10204
|
provider,
|
|
9484
10205
|
model
|
|
9485
10206
|
};
|
|
@@ -9513,79 +10234,16 @@ function truncateForWire(value) {
|
|
|
9513
10234
|
};
|
|
9514
10235
|
}
|
|
9515
10236
|
}
|
|
9516
|
-
/**
|
|
9517
|
-
* Find the last balanced top-level JSON object in `text` and parse it.
|
|
9518
|
-
* Tolerates markdown fences and leading prose. Returns null if parsing fails.
|
|
9519
|
-
*/
|
|
9520
|
-
function extractJsonObject(text) {
|
|
9521
|
-
if (!text) return null;
|
|
9522
|
-
const fenceMatch = /```(?:json)?\s*([\s\S]*?)```/gi;
|
|
9523
|
-
const candidates = [];
|
|
9524
|
-
for (const m of text.matchAll(fenceMatch)) candidates.push(m[1]);
|
|
9525
|
-
const scanForObject = (s) => {
|
|
9526
|
-
let depth = 0;
|
|
9527
|
-
let start = -1;
|
|
9528
|
-
let lastComplete = null;
|
|
9529
|
-
let inString = false;
|
|
9530
|
-
let escape = false;
|
|
9531
|
-
for (let i = 0; i < s.length; i++) {
|
|
9532
|
-
const ch = s[i];
|
|
9533
|
-
if (inString) {
|
|
9534
|
-
if (escape) escape = false;
|
|
9535
|
-
else if (ch === "\\") escape = true;
|
|
9536
|
-
else if (ch === "\"") inString = false;
|
|
9537
|
-
continue;
|
|
9538
|
-
}
|
|
9539
|
-
if (ch === "\"") {
|
|
9540
|
-
inString = true;
|
|
9541
|
-
continue;
|
|
9542
|
-
}
|
|
9543
|
-
if (ch === "{") {
|
|
9544
|
-
if (depth === 0) start = i;
|
|
9545
|
-
depth++;
|
|
9546
|
-
} else if (ch === "}") {
|
|
9547
|
-
depth--;
|
|
9548
|
-
if (depth === 0 && start !== -1) {
|
|
9549
|
-
lastComplete = s.slice(start, i + 1);
|
|
9550
|
-
start = -1;
|
|
9551
|
-
}
|
|
9552
|
-
}
|
|
9553
|
-
}
|
|
9554
|
-
return lastComplete;
|
|
9555
|
-
};
|
|
9556
|
-
candidates.push(text);
|
|
9557
|
-
for (let i = candidates.length - 1; i >= 0; i--) {
|
|
9558
|
-
const obj = scanForObject(candidates[i]);
|
|
9559
|
-
if (!obj) continue;
|
|
9560
|
-
try {
|
|
9561
|
-
return JSON.parse(obj);
|
|
9562
|
-
} catch {}
|
|
9563
|
-
}
|
|
9564
|
-
return null;
|
|
9565
|
-
}
|
|
9566
10237
|
//#endregion
|
|
9567
10238
|
//#region src/index.ts
|
|
9568
10239
|
/**
|
|
9569
10240
|
* @themoltnet/pi-extension — MoltNet pi extension
|
|
9570
10241
|
*
|
|
9571
|
-
*
|
|
9572
|
-
*
|
|
9573
|
-
* - Credential injection (pi OAuth + MoltNet identity)
|
|
9574
|
-
* - Egress policy (only LLM provider + MoltNet API)
|
|
9575
|
-
* - Tool redirection (read/write/edit/bash → VM)
|
|
9576
|
-
* - MoltNet custom tools (diary entries — run on host via SDK)
|
|
9577
|
-
* - Optional git worktree per session
|
|
10242
|
+
* Runs pi coding-agent sessions inside a Gondolin VM with the agent's
|
|
10243
|
+
* MoltNet identity fully available inside the sandbox.
|
|
9578
10244
|
*
|
|
9579
|
-
*
|
|
9580
|
-
*
|
|
9581
|
-
* pi -e @themoltnet/pi-extension --agent legreffier
|
|
9582
|
-
* pi -e @themoltnet/pi-extension --worktree-branch feat/my-task
|
|
9583
|
-
* pi -e @themoltnet/pi-extension --sandbox-config ./sandbox.json
|
|
9584
|
-
*
|
|
9585
|
-
* Sandbox config resolution (first match):
|
|
9586
|
-
* 1. --sandbox-config flag (explicit path to JSON)
|
|
9587
|
-
* 2. sandbox.json in cwd (convention)
|
|
9588
|
-
* 3. Base only (git, gh, moltnet CLI, agent user)
|
|
10245
|
+
* See README.md for credential injection flow, tool split, sandbox.json
|
|
10246
|
+
* reference, and headless/programmatic usage.
|
|
9589
10247
|
*/
|
|
9590
10248
|
var GUEST_WORKSPACE = "/workspace";
|
|
9591
10249
|
function moltnetExtension(pi) {
|
|
@@ -9623,6 +10281,7 @@ function moltnetExtension(pi) {
|
|
|
9623
10281
|
let worktreePath = null;
|
|
9624
10282
|
let moltnetAgent = null;
|
|
9625
10283
|
let diaryId = null;
|
|
10284
|
+
let hostExecBaseEnv = HOST_EXEC_DEFAULT_BASE_ENV;
|
|
9626
10285
|
async function ensureVm(ctx) {
|
|
9627
10286
|
if (vm) return vm;
|
|
9628
10287
|
if (vmStarting) return vmStarting;
|
|
@@ -9676,6 +10335,7 @@ function moltnetExtension(pi) {
|
|
|
9676
10335
|
activateAgentEnv(managed.credentials.agentEnv, mainRepo);
|
|
9677
10336
|
moltnetAgent = await connect({ configDir: managed.agentDir });
|
|
9678
10337
|
diaryId = managed.credentials.agentEnv.MOLTNET_DIARY_ID ?? null;
|
|
10338
|
+
hostExecBaseEnv = new Set([...HOST_EXEC_DEFAULT_BASE_ENV, ...Object.keys(managed.credentials.agentEnv)]);
|
|
9679
10339
|
vm = managed.vm;
|
|
9680
10340
|
const label = worktreePath ? `${mountPath} → ${GUEST_WORKSPACE}` : `${localCwd} → ${GUEST_WORKSPACE}`;
|
|
9681
10341
|
ctx?.ui.setStatus("sandbox", ctx.ui.theme.fg("accent", `Sandbox: running (${label})`));
|
|
@@ -9738,7 +10398,9 @@ function moltnetExtension(pi) {
|
|
|
9738
10398
|
getSessionErrors: () => sessionErrors,
|
|
9739
10399
|
clearSessionErrors: () => {
|
|
9740
10400
|
sessionErrors.length = 0;
|
|
9741
|
-
}
|
|
10401
|
+
},
|
|
10402
|
+
getHostCwd: () => worktreePath ?? localCwd,
|
|
10403
|
+
hostExecBaseEnv
|
|
9742
10404
|
});
|
|
9743
10405
|
for (const tool of moltnetTools) pi.registerTool(tool);
|
|
9744
10406
|
const sessionStartTime = Date.now();
|
|
@@ -9844,4 +10506,4 @@ function moltnetExtension(pi) {
|
|
|
9844
10506
|
registerMoltnetReflectCommand(pi, state);
|
|
9845
10507
|
}
|
|
9846
10508
|
//#endregion
|
|
9847
|
-
export { activateAgentEnv, buildPiJudgeRecipeManifest, computePiJudgeRecipeCid, createGondolinBashOps, createGondolinEditOps, createGondolinReadOps, createGondolinWriteOps, createMoltNetTools, createPiTaskExecutor, moltnetExtension as default, ensureSnapshot, executePiTask, findMainWorktree, loadCredentials, resolvePiJudgeRecipeVersions, resumeVm, toGuestPath };
|
|
10509
|
+
export { HOST_EXEC_DEFAULT_BASE_ENV, activateAgentEnv, buildPiJudgeRecipeManifest, computePiJudgeRecipeCid, createGondolinBashOps, createGondolinEditOps, createGondolinReadOps, createGondolinWriteOps, createMoltNetTools, createPiTaskExecutor, moltnetExtension as default, ensureSnapshot, executePiTask, findMainWorktree, loadCredentials, resolvePiJudgeRecipeVersions, resumeVm, toGuestPath };
|