@paigy/harness 0.3.31 → 0.3.33
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 +10 -4
- package/dist/cli.js +966 -464
- package/dist/main.js +733 -271
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -901,14 +901,14 @@ var require_regex = __commonJS({
|
|
|
901
901
|
var TEST_KANJI = new RegExp("^" + kanji + "$");
|
|
902
902
|
var TEST_NUMERIC = new RegExp("^" + numeric + "$");
|
|
903
903
|
var TEST_ALPHANUMERIC = new RegExp("^[A-Z0-9 $%*+\\-./:]+$");
|
|
904
|
-
exports.testKanji = function testKanji(
|
|
905
|
-
return TEST_KANJI.test(
|
|
904
|
+
exports.testKanji = function testKanji(str3) {
|
|
905
|
+
return TEST_KANJI.test(str3);
|
|
906
906
|
};
|
|
907
|
-
exports.testNumeric = function testNumeric(
|
|
908
|
-
return TEST_NUMERIC.test(
|
|
907
|
+
exports.testNumeric = function testNumeric(str3) {
|
|
908
|
+
return TEST_NUMERIC.test(str3);
|
|
909
909
|
};
|
|
910
|
-
exports.testAlphanumeric = function testAlphanumeric(
|
|
911
|
-
return TEST_ALPHANUMERIC.test(
|
|
910
|
+
exports.testAlphanumeric = function testAlphanumeric(str3) {
|
|
911
|
+
return TEST_ALPHANUMERIC.test(str3);
|
|
912
912
|
};
|
|
913
913
|
}
|
|
914
914
|
});
|
|
@@ -1358,11 +1358,11 @@ var require_dijkstra = __commonJS({
|
|
|
1358
1358
|
*/
|
|
1359
1359
|
PriorityQueue: {
|
|
1360
1360
|
make: function(opts) {
|
|
1361
|
-
var T = dijkstra.PriorityQueue, t = {},
|
|
1361
|
+
var T = dijkstra.PriorityQueue, t = {}, key3;
|
|
1362
1362
|
opts = opts || {};
|
|
1363
|
-
for (
|
|
1364
|
-
if (T.hasOwnProperty(
|
|
1365
|
-
t[
|
|
1363
|
+
for (key3 in T) {
|
|
1364
|
+
if (T.hasOwnProperty(key3)) {
|
|
1365
|
+
t[key3] = T[key3];
|
|
1366
1366
|
}
|
|
1367
1367
|
}
|
|
1368
1368
|
t.queue = [];
|
|
@@ -1410,13 +1410,13 @@ var require_segments = __commonJS({
|
|
|
1410
1410
|
var Regex = require_regex();
|
|
1411
1411
|
var Utils = require_utils();
|
|
1412
1412
|
var dijkstra = require_dijkstra();
|
|
1413
|
-
function getStringByteLength(
|
|
1414
|
-
return unescape(encodeURIComponent(
|
|
1413
|
+
function getStringByteLength(str3) {
|
|
1414
|
+
return unescape(encodeURIComponent(str3)).length;
|
|
1415
1415
|
}
|
|
1416
|
-
function getSegments(regex, mode,
|
|
1416
|
+
function getSegments(regex, mode, str3) {
|
|
1417
1417
|
const segments = [];
|
|
1418
1418
|
let result;
|
|
1419
|
-
while ((result = regex.exec(
|
|
1419
|
+
while ((result = regex.exec(str3)) !== null) {
|
|
1420
1420
|
segments.push({
|
|
1421
1421
|
data: result[0],
|
|
1422
1422
|
index: result.index,
|
|
@@ -1513,18 +1513,18 @@ var require_segments = __commonJS({
|
|
|
1513
1513
|
const currentNodeIds = [];
|
|
1514
1514
|
for (let j = 0; j < nodeGroup.length; j++) {
|
|
1515
1515
|
const node = nodeGroup[j];
|
|
1516
|
-
const
|
|
1517
|
-
currentNodeIds.push(
|
|
1518
|
-
table[
|
|
1519
|
-
graph[
|
|
1516
|
+
const key3 = "" + i + j;
|
|
1517
|
+
currentNodeIds.push(key3);
|
|
1518
|
+
table[key3] = { node, lastCount: 0 };
|
|
1519
|
+
graph[key3] = {};
|
|
1520
1520
|
for (let n = 0; n < prevNodeIds.length; n++) {
|
|
1521
1521
|
const prevNodeId = prevNodeIds[n];
|
|
1522
1522
|
if (table[prevNodeId] && table[prevNodeId].node.mode === node.mode) {
|
|
1523
|
-
graph[prevNodeId][
|
|
1523
|
+
graph[prevNodeId][key3] = getSegmentBitsLength(table[prevNodeId].lastCount + node.length, node.mode) - getSegmentBitsLength(table[prevNodeId].lastCount, node.mode);
|
|
1524
1524
|
table[prevNodeId].lastCount += node.length;
|
|
1525
1525
|
} else {
|
|
1526
1526
|
if (table[prevNodeId]) table[prevNodeId].lastCount = node.length;
|
|
1527
|
-
graph[prevNodeId][
|
|
1527
|
+
graph[prevNodeId][key3] = getSegmentBitsLength(node.length, node.mode) + 4 + Mode.getCharCountIndicator(node.mode, version);
|
|
1528
1528
|
}
|
|
1529
1529
|
}
|
|
1530
1530
|
}
|
|
@@ -4250,13 +4250,13 @@ var require_svg_tag = __commonJS({
|
|
|
4250
4250
|
var Utils = require_utils2();
|
|
4251
4251
|
function getColorAttrib(color, attrib) {
|
|
4252
4252
|
const alpha = color.a / 255;
|
|
4253
|
-
const
|
|
4254
|
-
return alpha < 1 ?
|
|
4253
|
+
const str3 = attrib + '="' + color.hex + '"';
|
|
4254
|
+
return alpha < 1 ? str3 + " " + attrib + '-opacity="' + alpha.toFixed(2).slice(1) + '"' : str3;
|
|
4255
4255
|
}
|
|
4256
4256
|
function svgCmd(cmd, x, y) {
|
|
4257
|
-
let
|
|
4258
|
-
if (typeof y !== "undefined")
|
|
4259
|
-
return
|
|
4257
|
+
let str3 = cmd + x;
|
|
4258
|
+
if (typeof y !== "undefined") str3 += " " + y;
|
|
4259
|
+
return str3;
|
|
4260
4260
|
}
|
|
4261
4261
|
function qrToPath(data, size, margin) {
|
|
4262
4262
|
let path = "";
|
|
@@ -4578,9 +4578,9 @@ var require_lib = __commonJS({
|
|
|
4578
4578
|
// src/main.ts
|
|
4579
4579
|
import { app, BrowserWindow, dialog, ipcMain } from "electron";
|
|
4580
4580
|
import { spawn as spawn2 } from "child_process";
|
|
4581
|
-
import { readFileSync as
|
|
4581
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
4582
4582
|
import { fileURLToPath } from "url";
|
|
4583
|
-
import { dirname as dirname4, join as
|
|
4583
|
+
import { dirname as dirname4, join as join12 } from "path";
|
|
4584
4584
|
|
|
4585
4585
|
// ../../packages/sdk/dist/index.js
|
|
4586
4586
|
import { createRequire as __sdkCreateRequire } from "module";
|
|
@@ -4738,9 +4738,9 @@ var util;
|
|
|
4738
4738
|
};
|
|
4739
4739
|
util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object) => {
|
|
4740
4740
|
const keys = [];
|
|
4741
|
-
for (const
|
|
4742
|
-
if (Object.prototype.hasOwnProperty.call(object,
|
|
4743
|
-
keys.push(
|
|
4741
|
+
for (const key3 in object) {
|
|
4742
|
+
if (Object.prototype.hasOwnProperty.call(object, key3)) {
|
|
4743
|
+
keys.push(key3);
|
|
4744
4744
|
}
|
|
4745
4745
|
}
|
|
4746
4746
|
return keys;
|
|
@@ -5140,10 +5140,10 @@ var ParseStatus = class _ParseStatus {
|
|
|
5140
5140
|
static async mergeObjectAsync(status, pairs) {
|
|
5141
5141
|
const syncPairs = [];
|
|
5142
5142
|
for (const pair of pairs) {
|
|
5143
|
-
const
|
|
5143
|
+
const key3 = await pair.key;
|
|
5144
5144
|
const value = await pair.value;
|
|
5145
5145
|
syncPairs.push({
|
|
5146
|
-
key,
|
|
5146
|
+
key: key3,
|
|
5147
5147
|
value
|
|
5148
5148
|
});
|
|
5149
5149
|
}
|
|
@@ -5152,17 +5152,17 @@ var ParseStatus = class _ParseStatus {
|
|
|
5152
5152
|
static mergeObjectSync(status, pairs) {
|
|
5153
5153
|
const finalObject = {};
|
|
5154
5154
|
for (const pair of pairs) {
|
|
5155
|
-
const { key, value } = pair;
|
|
5156
|
-
if (
|
|
5155
|
+
const { key: key3, value } = pair;
|
|
5156
|
+
if (key3.status === "aborted")
|
|
5157
5157
|
return INVALID;
|
|
5158
5158
|
if (value.status === "aborted")
|
|
5159
5159
|
return INVALID;
|
|
5160
|
-
if (
|
|
5160
|
+
if (key3.status === "dirty")
|
|
5161
5161
|
status.dirty();
|
|
5162
5162
|
if (value.status === "dirty")
|
|
5163
5163
|
status.dirty();
|
|
5164
|
-
if (
|
|
5165
|
-
finalObject[
|
|
5164
|
+
if (key3.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) {
|
|
5165
|
+
finalObject[key3.value] = value.value;
|
|
5166
5166
|
}
|
|
5167
5167
|
}
|
|
5168
5168
|
return { status: status.value, value: finalObject };
|
|
@@ -5187,12 +5187,12 @@ var errorUtil;
|
|
|
5187
5187
|
|
|
5188
5188
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
5189
5189
|
var ParseInputLazyPath = class {
|
|
5190
|
-
constructor(parent, value, path,
|
|
5190
|
+
constructor(parent, value, path, key3) {
|
|
5191
5191
|
this._cachedPath = [];
|
|
5192
5192
|
this.parent = parent;
|
|
5193
5193
|
this.data = value;
|
|
5194
5194
|
this._path = path;
|
|
5195
|
-
this._key =
|
|
5195
|
+
this._key = key3;
|
|
5196
5196
|
}
|
|
5197
5197
|
get path() {
|
|
5198
5198
|
if (!this._cachedPath.length) {
|
|
@@ -5471,8 +5471,8 @@ var ZodType = class {
|
|
|
5471
5471
|
promise() {
|
|
5472
5472
|
return ZodPromise.create(this, this._def);
|
|
5473
5473
|
}
|
|
5474
|
-
or(
|
|
5475
|
-
return ZodUnion.create([this,
|
|
5474
|
+
or(option3) {
|
|
5475
|
+
return ZodUnion.create([this, option3], this._def);
|
|
5476
5476
|
}
|
|
5477
5477
|
and(incoming) {
|
|
5478
5478
|
return ZodIntersection.create(this, incoming, this._def);
|
|
@@ -6937,9 +6937,9 @@ ZodArray.create = (schema, params) => {
|
|
|
6937
6937
|
function deepPartialify(schema) {
|
|
6938
6938
|
if (schema instanceof ZodObject) {
|
|
6939
6939
|
const newShape = {};
|
|
6940
|
-
for (const
|
|
6941
|
-
const fieldSchema = schema.shape[
|
|
6942
|
-
newShape[
|
|
6940
|
+
for (const key3 in schema.shape) {
|
|
6941
|
+
const fieldSchema = schema.shape[key3];
|
|
6942
|
+
newShape[key3] = ZodOptional.create(deepPartialify(fieldSchema));
|
|
6943
6943
|
}
|
|
6944
6944
|
return new ZodObject({
|
|
6945
6945
|
...schema._def,
|
|
@@ -6990,29 +6990,29 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
6990
6990
|
const { shape, keys: shapeKeys } = this._getCached();
|
|
6991
6991
|
const extraKeys = [];
|
|
6992
6992
|
if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) {
|
|
6993
|
-
for (const
|
|
6994
|
-
if (!shapeKeys.includes(
|
|
6995
|
-
extraKeys.push(
|
|
6993
|
+
for (const key3 in ctx.data) {
|
|
6994
|
+
if (!shapeKeys.includes(key3)) {
|
|
6995
|
+
extraKeys.push(key3);
|
|
6996
6996
|
}
|
|
6997
6997
|
}
|
|
6998
6998
|
}
|
|
6999
6999
|
const pairs = [];
|
|
7000
|
-
for (const
|
|
7001
|
-
const keyValidator = shape[
|
|
7002
|
-
const value = ctx.data[
|
|
7000
|
+
for (const key3 of shapeKeys) {
|
|
7001
|
+
const keyValidator = shape[key3];
|
|
7002
|
+
const value = ctx.data[key3];
|
|
7003
7003
|
pairs.push({
|
|
7004
|
-
key: { status: "valid", value:
|
|
7005
|
-
value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path,
|
|
7006
|
-
alwaysSet:
|
|
7004
|
+
key: { status: "valid", value: key3 },
|
|
7005
|
+
value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key3)),
|
|
7006
|
+
alwaysSet: key3 in ctx.data
|
|
7007
7007
|
});
|
|
7008
7008
|
}
|
|
7009
7009
|
if (this._def.catchall instanceof ZodNever) {
|
|
7010
7010
|
const unknownKeys = this._def.unknownKeys;
|
|
7011
7011
|
if (unknownKeys === "passthrough") {
|
|
7012
|
-
for (const
|
|
7012
|
+
for (const key3 of extraKeys) {
|
|
7013
7013
|
pairs.push({
|
|
7014
|
-
key: { status: "valid", value:
|
|
7015
|
-
value: { status: "valid", value: ctx.data[
|
|
7014
|
+
key: { status: "valid", value: key3 },
|
|
7015
|
+
value: { status: "valid", value: ctx.data[key3] }
|
|
7016
7016
|
});
|
|
7017
7017
|
}
|
|
7018
7018
|
} else if (unknownKeys === "strict") {
|
|
@@ -7029,15 +7029,15 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
7029
7029
|
}
|
|
7030
7030
|
} else {
|
|
7031
7031
|
const catchall = this._def.catchall;
|
|
7032
|
-
for (const
|
|
7033
|
-
const value = ctx.data[
|
|
7032
|
+
for (const key3 of extraKeys) {
|
|
7033
|
+
const value = ctx.data[key3];
|
|
7034
7034
|
pairs.push({
|
|
7035
|
-
key: { status: "valid", value:
|
|
7035
|
+
key: { status: "valid", value: key3 },
|
|
7036
7036
|
value: catchall._parse(
|
|
7037
|
-
new ParseInputLazyPath(ctx, value, ctx.path,
|
|
7037
|
+
new ParseInputLazyPath(ctx, value, ctx.path, key3)
|
|
7038
7038
|
//, ctx.child(key), value, getParsedType(value)
|
|
7039
7039
|
),
|
|
7040
|
-
alwaysSet:
|
|
7040
|
+
alwaysSet: key3 in ctx.data
|
|
7041
7041
|
});
|
|
7042
7042
|
}
|
|
7043
7043
|
}
|
|
@@ -7045,10 +7045,10 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
7045
7045
|
return Promise.resolve().then(async () => {
|
|
7046
7046
|
const syncPairs = [];
|
|
7047
7047
|
for (const pair of pairs) {
|
|
7048
|
-
const
|
|
7048
|
+
const key3 = await pair.key;
|
|
7049
7049
|
const value = await pair.value;
|
|
7050
7050
|
syncPairs.push({
|
|
7051
|
-
key,
|
|
7051
|
+
key: key3,
|
|
7052
7052
|
value,
|
|
7053
7053
|
alwaysSet: pair.alwaysSet
|
|
7054
7054
|
});
|
|
@@ -7173,8 +7173,8 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
7173
7173
|
// }) as any;
|
|
7174
7174
|
// return merged;
|
|
7175
7175
|
// }
|
|
7176
|
-
setKey(
|
|
7177
|
-
return this.augment({ [
|
|
7176
|
+
setKey(key3, schema) {
|
|
7177
|
+
return this.augment({ [key3]: schema });
|
|
7178
7178
|
}
|
|
7179
7179
|
// merge<Incoming extends AnyZodObject>(
|
|
7180
7180
|
// merging: Incoming
|
|
@@ -7205,9 +7205,9 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
7205
7205
|
}
|
|
7206
7206
|
pick(mask) {
|
|
7207
7207
|
const shape = {};
|
|
7208
|
-
for (const
|
|
7209
|
-
if (mask[
|
|
7210
|
-
shape[
|
|
7208
|
+
for (const key3 of util.objectKeys(mask)) {
|
|
7209
|
+
if (mask[key3] && this.shape[key3]) {
|
|
7210
|
+
shape[key3] = this.shape[key3];
|
|
7211
7211
|
}
|
|
7212
7212
|
}
|
|
7213
7213
|
return new _ZodObject({
|
|
@@ -7217,9 +7217,9 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
7217
7217
|
}
|
|
7218
7218
|
omit(mask) {
|
|
7219
7219
|
const shape = {};
|
|
7220
|
-
for (const
|
|
7221
|
-
if (!mask[
|
|
7222
|
-
shape[
|
|
7220
|
+
for (const key3 of util.objectKeys(this.shape)) {
|
|
7221
|
+
if (!mask[key3]) {
|
|
7222
|
+
shape[key3] = this.shape[key3];
|
|
7223
7223
|
}
|
|
7224
7224
|
}
|
|
7225
7225
|
return new _ZodObject({
|
|
@@ -7235,12 +7235,12 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
7235
7235
|
}
|
|
7236
7236
|
partial(mask) {
|
|
7237
7237
|
const newShape = {};
|
|
7238
|
-
for (const
|
|
7239
|
-
const fieldSchema = this.shape[
|
|
7240
|
-
if (mask && !mask[
|
|
7241
|
-
newShape[
|
|
7238
|
+
for (const key3 of util.objectKeys(this.shape)) {
|
|
7239
|
+
const fieldSchema = this.shape[key3];
|
|
7240
|
+
if (mask && !mask[key3]) {
|
|
7241
|
+
newShape[key3] = fieldSchema;
|
|
7242
7242
|
} else {
|
|
7243
|
-
newShape[
|
|
7243
|
+
newShape[key3] = fieldSchema.optional();
|
|
7244
7244
|
}
|
|
7245
7245
|
}
|
|
7246
7246
|
return new _ZodObject({
|
|
@@ -7250,16 +7250,16 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
7250
7250
|
}
|
|
7251
7251
|
required(mask) {
|
|
7252
7252
|
const newShape = {};
|
|
7253
|
-
for (const
|
|
7254
|
-
if (mask && !mask[
|
|
7255
|
-
newShape[
|
|
7253
|
+
for (const key3 of util.objectKeys(this.shape)) {
|
|
7254
|
+
if (mask && !mask[key3]) {
|
|
7255
|
+
newShape[key3] = this.shape[key3];
|
|
7256
7256
|
} else {
|
|
7257
|
-
const fieldSchema = this.shape[
|
|
7257
|
+
const fieldSchema = this.shape[key3];
|
|
7258
7258
|
let newField = fieldSchema;
|
|
7259
7259
|
while (newField instanceof ZodOptional) {
|
|
7260
7260
|
newField = newField._def.innerType;
|
|
7261
7261
|
}
|
|
7262
|
-
newShape[
|
|
7262
|
+
newShape[key3] = newField;
|
|
7263
7263
|
}
|
|
7264
7264
|
}
|
|
7265
7265
|
return new _ZodObject({
|
|
@@ -7322,7 +7322,7 @@ var ZodUnion = class extends ZodType {
|
|
|
7322
7322
|
return INVALID;
|
|
7323
7323
|
}
|
|
7324
7324
|
if (ctx.common.async) {
|
|
7325
|
-
return Promise.all(options.map(async (
|
|
7325
|
+
return Promise.all(options.map(async (option3) => {
|
|
7326
7326
|
const childCtx = {
|
|
7327
7327
|
...ctx,
|
|
7328
7328
|
common: {
|
|
@@ -7332,7 +7332,7 @@ var ZodUnion = class extends ZodType {
|
|
|
7332
7332
|
parent: null
|
|
7333
7333
|
};
|
|
7334
7334
|
return {
|
|
7335
|
-
result: await
|
|
7335
|
+
result: await option3._parseAsync({
|
|
7336
7336
|
data: ctx.data,
|
|
7337
7337
|
path: ctx.path,
|
|
7338
7338
|
parent: childCtx
|
|
@@ -7343,7 +7343,7 @@ var ZodUnion = class extends ZodType {
|
|
|
7343
7343
|
} else {
|
|
7344
7344
|
let dirty = void 0;
|
|
7345
7345
|
const issues = [];
|
|
7346
|
-
for (const
|
|
7346
|
+
for (const option3 of options) {
|
|
7347
7347
|
const childCtx = {
|
|
7348
7348
|
...ctx,
|
|
7349
7349
|
common: {
|
|
@@ -7352,7 +7352,7 @@ var ZodUnion = class extends ZodType {
|
|
|
7352
7352
|
},
|
|
7353
7353
|
parent: null
|
|
7354
7354
|
};
|
|
7355
|
-
const result =
|
|
7355
|
+
const result = option3._parseSync({
|
|
7356
7356
|
data: ctx.data,
|
|
7357
7357
|
path: ctx.path,
|
|
7358
7358
|
parent: childCtx
|
|
@@ -7433,8 +7433,8 @@ var ZodDiscriminatedUnion = class _ZodDiscriminatedUnion extends ZodType {
|
|
|
7433
7433
|
}
|
|
7434
7434
|
const discriminator = this.discriminator;
|
|
7435
7435
|
const discriminatorValue = ctx.data[discriminator];
|
|
7436
|
-
const
|
|
7437
|
-
if (!
|
|
7436
|
+
const option3 = this.optionsMap.get(discriminatorValue);
|
|
7437
|
+
if (!option3) {
|
|
7438
7438
|
addIssueToContext(ctx, {
|
|
7439
7439
|
code: ZodIssueCode.invalid_union_discriminator,
|
|
7440
7440
|
options: Array.from(this.optionsMap.keys()),
|
|
@@ -7443,13 +7443,13 @@ var ZodDiscriminatedUnion = class _ZodDiscriminatedUnion extends ZodType {
|
|
|
7443
7443
|
return INVALID;
|
|
7444
7444
|
}
|
|
7445
7445
|
if (ctx.common.async) {
|
|
7446
|
-
return
|
|
7446
|
+
return option3._parseAsync({
|
|
7447
7447
|
data: ctx.data,
|
|
7448
7448
|
path: ctx.path,
|
|
7449
7449
|
parent: ctx
|
|
7450
7450
|
});
|
|
7451
7451
|
} else {
|
|
7452
|
-
return
|
|
7452
|
+
return option3._parseSync({
|
|
7453
7453
|
data: ctx.data,
|
|
7454
7454
|
path: ctx.path,
|
|
7455
7455
|
parent: ctx
|
|
@@ -7503,14 +7503,14 @@ function mergeValues(a, b) {
|
|
|
7503
7503
|
return { valid: true, data: a };
|
|
7504
7504
|
} else if (aType === ZodParsedType.object && bType === ZodParsedType.object) {
|
|
7505
7505
|
const bKeys = util.objectKeys(b);
|
|
7506
|
-
const sharedKeys = util.objectKeys(a).filter((
|
|
7506
|
+
const sharedKeys = util.objectKeys(a).filter((key3) => bKeys.indexOf(key3) !== -1);
|
|
7507
7507
|
const newObj = { ...a, ...b };
|
|
7508
|
-
for (const
|
|
7509
|
-
const sharedValue = mergeValues(a[
|
|
7508
|
+
for (const key3 of sharedKeys) {
|
|
7509
|
+
const sharedValue = mergeValues(a[key3], b[key3]);
|
|
7510
7510
|
if (!sharedValue.valid) {
|
|
7511
7511
|
return { valid: false };
|
|
7512
7512
|
}
|
|
7513
|
-
newObj[
|
|
7513
|
+
newObj[key3] = sharedValue.data;
|
|
7514
7514
|
}
|
|
7515
7515
|
return { valid: true, data: newObj };
|
|
7516
7516
|
} else if (aType === ZodParsedType.array && bType === ZodParsedType.array) {
|
|
@@ -7674,11 +7674,11 @@ var ZodRecord = class _ZodRecord extends ZodType {
|
|
|
7674
7674
|
const pairs = [];
|
|
7675
7675
|
const keyType = this._def.keyType;
|
|
7676
7676
|
const valueType = this._def.valueType;
|
|
7677
|
-
for (const
|
|
7677
|
+
for (const key3 in ctx.data) {
|
|
7678
7678
|
pairs.push({
|
|
7679
|
-
key: keyType._parse(new ParseInputLazyPath(ctx,
|
|
7680
|
-
value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[
|
|
7681
|
-
alwaysSet:
|
|
7679
|
+
key: keyType._parse(new ParseInputLazyPath(ctx, key3, ctx.path, key3)),
|
|
7680
|
+
value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key3], ctx.path, key3)),
|
|
7681
|
+
alwaysSet: key3 in ctx.data
|
|
7682
7682
|
});
|
|
7683
7683
|
}
|
|
7684
7684
|
if (ctx.common.async) {
|
|
@@ -7726,9 +7726,9 @@ var ZodMap = class extends ZodType {
|
|
|
7726
7726
|
}
|
|
7727
7727
|
const keyType = this._def.keyType;
|
|
7728
7728
|
const valueType = this._def.valueType;
|
|
7729
|
-
const pairs = [...ctx.data.entries()].map(([
|
|
7729
|
+
const pairs = [...ctx.data.entries()].map(([key3, value], index) => {
|
|
7730
7730
|
return {
|
|
7731
|
-
key: keyType._parse(new ParseInputLazyPath(ctx,
|
|
7731
|
+
key: keyType._parse(new ParseInputLazyPath(ctx, key3, ctx.path, [index, "key"])),
|
|
7732
7732
|
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"]))
|
|
7733
7733
|
};
|
|
7734
7734
|
});
|
|
@@ -7736,30 +7736,30 @@ var ZodMap = class extends ZodType {
|
|
|
7736
7736
|
const finalMap = /* @__PURE__ */ new Map();
|
|
7737
7737
|
return Promise.resolve().then(async () => {
|
|
7738
7738
|
for (const pair of pairs) {
|
|
7739
|
-
const
|
|
7739
|
+
const key3 = await pair.key;
|
|
7740
7740
|
const value = await pair.value;
|
|
7741
|
-
if (
|
|
7741
|
+
if (key3.status === "aborted" || value.status === "aborted") {
|
|
7742
7742
|
return INVALID;
|
|
7743
7743
|
}
|
|
7744
|
-
if (
|
|
7744
|
+
if (key3.status === "dirty" || value.status === "dirty") {
|
|
7745
7745
|
status.dirty();
|
|
7746
7746
|
}
|
|
7747
|
-
finalMap.set(
|
|
7747
|
+
finalMap.set(key3.value, value.value);
|
|
7748
7748
|
}
|
|
7749
7749
|
return { status: status.value, value: finalMap };
|
|
7750
7750
|
});
|
|
7751
7751
|
} else {
|
|
7752
7752
|
const finalMap = /* @__PURE__ */ new Map();
|
|
7753
7753
|
for (const pair of pairs) {
|
|
7754
|
-
const
|
|
7754
|
+
const key3 = pair.key;
|
|
7755
7755
|
const value = pair.value;
|
|
7756
|
-
if (
|
|
7756
|
+
if (key3.status === "aborted" || value.status === "aborted") {
|
|
7757
7757
|
return INVALID;
|
|
7758
7758
|
}
|
|
7759
|
-
if (
|
|
7759
|
+
if (key3.status === "dirty" || value.status === "dirty") {
|
|
7760
7760
|
status.dirty();
|
|
7761
7761
|
}
|
|
7762
|
-
finalMap.set(
|
|
7762
|
+
finalMap.set(key3.value, value.value);
|
|
7763
7763
|
}
|
|
7764
7764
|
return { status: status.value, value: finalMap };
|
|
7765
7765
|
}
|
|
@@ -8641,6 +8641,7 @@ import { join as join2 } from "path";
|
|
|
8641
8641
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
8642
8642
|
var import_undici = __toESM(require_undici(), 1);
|
|
8643
8643
|
var require2 = __sdkCreateRequire(import.meta.url);
|
|
8644
|
+
var CODEX_ENV = ["CODEX_THREAD_ID", "CODEX_SESSION_ID", "CODEX_HOME", "PAIGY_TOKEN", "PAIGY_SESSION_ID", "PAIGY_HARNESS", "PAIGY_INSTANCE_ID", "PAIGY_ON_WAKE"];
|
|
8644
8645
|
var BACKEND_URL = process.env.PAIGY_BACKEND_URL ?? "https://paigy.ai";
|
|
8645
8646
|
var NETWORK_MSG = `Can't reach ${BACKEND_URL} \u2014 the connection was blocked or dropped before an HTTP response. If this agent runs in a sandboxed environment with a network allowlist (e.g. Claude Code on the web, CI), ask the user to add the Paigy domain (paigy.ai) to the environment's allowed domains, then retry.`;
|
|
8646
8647
|
var PROXY_ENV = ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"];
|
|
@@ -8657,8 +8658,8 @@ var WORKSPACE_ID = (() => {
|
|
|
8657
8658
|
return INSTANCE_ID;
|
|
8658
8659
|
}
|
|
8659
8660
|
})();
|
|
8660
|
-
var envSession = (
|
|
8661
|
-
const v = process.env[
|
|
8661
|
+
var envSession = (key22) => {
|
|
8662
|
+
const v = process.env[key22];
|
|
8662
8663
|
return v && v.trim() ? v : void 0;
|
|
8663
8664
|
};
|
|
8664
8665
|
function parentOf(pid) {
|
|
@@ -8695,15 +8696,17 @@ function sessionId() {
|
|
|
8695
8696
|
async function reach(url, init) {
|
|
8696
8697
|
return send(url, init, sessionId());
|
|
8697
8698
|
}
|
|
8698
|
-
function reachAs(session) {
|
|
8699
|
-
return (url, init) => send(url, init, session);
|
|
8699
|
+
function reachAs(session, instance) {
|
|
8700
|
+
return (url, init) => send(url, init, session, instance);
|
|
8700
8701
|
}
|
|
8701
|
-
|
|
8702
|
+
var CLIENT;
|
|
8703
|
+
async function send(url, init, session, instance = envSession("PAIGY_INSTANCE_ID") ?? INSTANCE_ID) {
|
|
8702
8704
|
try {
|
|
8703
8705
|
const headers = {
|
|
8704
8706
|
...init?.headers,
|
|
8705
|
-
"x-paigy-instance":
|
|
8706
|
-
"x-paigy-session": session
|
|
8707
|
+
"x-paigy-instance": instance,
|
|
8708
|
+
"x-paigy-session": session,
|
|
8709
|
+
...CLIENT ? { "x-paigy-client": CLIENT } : {}
|
|
8707
8710
|
};
|
|
8708
8711
|
return await fetch(url, { ...init, headers, dispatcher: await proxy() });
|
|
8709
8712
|
} catch (e) {
|
|
@@ -8761,19 +8764,19 @@ var getRefs = (options) => {
|
|
|
8761
8764
|
]))
|
|
8762
8765
|
};
|
|
8763
8766
|
};
|
|
8764
|
-
function addErrorMessage(res,
|
|
8767
|
+
function addErrorMessage(res, key22, errorMessage, refs) {
|
|
8765
8768
|
if (!refs?.errorMessages)
|
|
8766
8769
|
return;
|
|
8767
8770
|
if (errorMessage) {
|
|
8768
8771
|
res.errorMessage = {
|
|
8769
8772
|
...res.errorMessage,
|
|
8770
|
-
[
|
|
8773
|
+
[key22]: errorMessage
|
|
8771
8774
|
};
|
|
8772
8775
|
}
|
|
8773
8776
|
}
|
|
8774
|
-
function setResponseValueAndErrors(res,
|
|
8775
|
-
res[
|
|
8776
|
-
addErrorMessage(res,
|
|
8777
|
+
function setResponseValueAndErrors(res, key22, value, errorMessage, refs) {
|
|
8778
|
+
res[key22] = value;
|
|
8779
|
+
addErrorMessage(res, key22, errorMessage, refs);
|
|
8777
8780
|
}
|
|
8778
8781
|
var getRelativePath = (pathA, pathB) => {
|
|
8779
8782
|
let i = 0;
|
|
@@ -9335,11 +9338,11 @@ function parseRecordDef(def, refs) {
|
|
|
9335
9338
|
return {
|
|
9336
9339
|
type: "object",
|
|
9337
9340
|
required: def.keyType._def.values,
|
|
9338
|
-
properties: def.keyType._def.values.reduce((acc,
|
|
9341
|
+
properties: def.keyType._def.values.reduce((acc, key22) => ({
|
|
9339
9342
|
...acc,
|
|
9340
|
-
[
|
|
9343
|
+
[key22]: parseDef(def.valueType._def, {
|
|
9341
9344
|
...refs,
|
|
9342
|
-
currentPath: [...refs.currentPath, "properties",
|
|
9345
|
+
currentPath: [...refs.currentPath, "properties", key22]
|
|
9343
9346
|
}) ?? parseAnyDef(refs)
|
|
9344
9347
|
}), {}),
|
|
9345
9348
|
additionalProperties: refs.rejectedAdditionalProperties
|
|
@@ -9402,10 +9405,10 @@ function parseMapDef(def, refs) {
|
|
|
9402
9405
|
}
|
|
9403
9406
|
function parseNativeEnumDef(def) {
|
|
9404
9407
|
const object = def.values;
|
|
9405
|
-
const actualKeys = Object.keys(def.values).filter((
|
|
9406
|
-
return typeof object[object[
|
|
9408
|
+
const actualKeys = Object.keys(def.values).filter((key22) => {
|
|
9409
|
+
return typeof object[object[key22]] !== "number";
|
|
9407
9410
|
});
|
|
9408
|
-
const actualValues = actualKeys.map((
|
|
9411
|
+
const actualValues = actualKeys.map((key22) => object[key22]);
|
|
9409
9412
|
const parsedTypes = Array.from(new Set(actualValues.map((values) => typeof values)));
|
|
9410
9413
|
return {
|
|
9411
9414
|
type: parsedTypes.length === 1 ? parsedTypes[0] === "string" ? "string" : "number" : ["string", "number"],
|
|
@@ -9970,34 +9973,31 @@ function mcpInputSchema(s) {
|
|
|
9970
9973
|
}
|
|
9971
9974
|
var AskInputSchema = external_exports.object({
|
|
9972
9975
|
id: external_exports.string().optional().describe("Optional idempotency key or client-side ID for this specific ask."),
|
|
9973
|
-
parentId: external_exports.string().uuid().optional().describe("The Goal this question is about \u2014 usually the one you are working on. The question goes onto that Goal and its answer comes back there. Omit it and
|
|
9976
|
+
parentId: external_exports.string().uuid().optional().describe("The Goal this question is about \u2014 usually the one you are working on. The question goes onto that Goal and its answer comes back there. Omit it and the question starts a new Goal of yours."),
|
|
9974
9977
|
repo: external_exports.string().optional().describe("Optional repository context."),
|
|
9975
9978
|
ask: external_exports.string().trim().min(1).max(1e4).describe(
|
|
9976
|
-
"ONE question, and only what is needed to answer it. Several questions are several asks in the array, one each: an answer settles the one ask it was given in the shape that ask declares, so a person who answers the part of a bundled ask that interested them settles nothing and is asked again. Paigy
|
|
9979
|
+
"ONE question, and only what is needed to answer it. Several questions are several asks in the array, one each: an answer settles the one ask it was given in the shape that ask declares, so a person who answers the part of a bundled ask that interested them settles nothing and is asked again. Paigy sends each ask exactly as you wrote it: a bundled ask arrives as one card. News, progress and findings are their own contact; ANY contact for a person already on a call JOINS that call, whatever channel you asked for, so several arrive as one call."
|
|
9977
9980
|
),
|
|
9978
9981
|
options: external_exports.array(OptionInputSchema).min(1).max(6).optional(),
|
|
9979
|
-
//
|
|
9980
|
-
//
|
|
9981
|
-
//
|
|
9982
|
-
//
|
|
9983
|
-
// as "All three / 1 and 3 only / Just log it", which the person could not read without the text and
|
|
9984
|
-
// could not answer as the ticks he wanted. "many" is the one shape an agent knows and the read does
|
|
9985
|
-
// not: whether its own options exclude each other.
|
|
9982
|
+
// HOW THE OPTIONS ARE ANSWERED, SAID BY THE AGENT (owner, 2026-10-02: "one and many makes sense";
|
|
9983
|
+
// brain_prompts.md §1: structured agent questions need no model). Every card was pick-one while no
|
|
9984
|
+
// agent sent this; no read reshapes cards any more, so this is the card's shape, and the
|
|
9985
|
+
// description says so plainly.
|
|
9986
9986
|
select: external_exports.enum(["one", "many"]).optional().describe(
|
|
9987
|
-
'How the options are answered: "
|
|
9987
|
+
'How the options are answered: "many" makes a checklist (they may want several, e.g. independent fixes), "one" (the default) a pick (choosing one rules out the others). Send "many" whenever more than one option could be wanted at once. The person can always answer in their own words as well. Ignored without options.'
|
|
9988
9988
|
),
|
|
9989
9989
|
answers: external_exports.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/).optional().describe(
|
|
9990
9990
|
"Your reply answers a question the person asked you: the id the conversation shows for it (8 characters or whole). Their question closes with this reply as its answer, and any decision of yours it was holding back goes back to them. Needs parentId: the Goal the question is on. The reply is sent to them as it is; to also ask something new, send that as its own ask."
|
|
9991
9991
|
)
|
|
9992
9992
|
}).strict();
|
|
9993
9993
|
var StartContactSchema = external_exports.object({
|
|
9994
|
-
asks: external_exports.array(AskInputSchema).min(1).describe("The questions to pose, one per object. An ask with a parentId is filed on that Goal as it is;
|
|
9994
|
+
asks: external_exports.array(AskInputSchema).min(1).describe("The questions to pose, one per object. An ask with a parentId is filed on that Goal as it is; an ask naming no Goal starts a new Goal of yours."),
|
|
9995
9995
|
waiting: external_exports.enum(["none", "hard"]).default("none").describe("hard requests a call even when channel is notification; user permissions and ring cooldowns still apply."),
|
|
9996
9996
|
channel: external_exports.enum(["notification", "call"]).default("notification")
|
|
9997
9997
|
}).strict();
|
|
9998
9998
|
var ContactSchema = external_exports.union([StartContactSchema, external_exports.object({ deliveryId: external_exports.string().uuid() }).strict()]);
|
|
9999
9999
|
var CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
|
|
10000
|
-
var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'select', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none
|
|
10000
|
+
var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'select', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none starts a new Goal of yours. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. One question per 'ask'; several questions are several objects in 'asks'. Each ask is sent exactly as written, with no reading in between: a bundled ask arrives as one card, so separate questions are separate asks. An ask with no options that is not blocking is a report, and a report is an UPDATE, never a claim on them: it reaches them and is listed apart from what waits on them, so no answer is owed and none should be awaited. On a Goal whose report card is still open it is added to that card, with no new push; Where your work stands while under way is progress: report it with update_goal, not contact. Send options (or waiting:'hard') when you actually need an answer. Each option's label must stand on its own \u2014 the person may see only the labels \u2014 so never a label that points into your text ('All three', 'Option 2', '1 and 3 only'). select:'many' makes the card a checklist, for independent options they may want several of; select:'one' (the default) a pick, for alternatives. The person can always answer in their own words, so never add an 'Other' option. If the person is already on a call, your contact joins that call automatically \u2014 whatever channel you asked for, with no ring \u2014 and the Delivery it returns IS that call: reread it with contact({deliveryId}) to see everything answered on it so far, and contact again while it is live to add information or a further question to the same call.";
|
|
10001
10001
|
var CreateGoalSchema = external_exports.object({
|
|
10002
10002
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
10003
10003
|
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
@@ -10020,7 +10020,7 @@ var CreateGoalSchema = external_exports.object({
|
|
|
10020
10020
|
var CreateGoalToolSchema = CreateGoalSchema.extend({
|
|
10021
10021
|
idempotencyKey: CreateGoalSchema.shape.idempotencyKey.optional().describe("Optional. One is minted per call; pass your own only so a retry lands on the same Goal.")
|
|
10022
10022
|
}).strict();
|
|
10023
|
-
var CREATE_GOAL_DESCRIPTION = 'Create a durable Goal for an outcome. Without parentGoalId it is placed against your open Goals: if one already IS this work, that Goal comes back (existing: true) and nothing new is created \u2014 continue it; if the work belongs under one, it is created there (parentGoalId in the receipt); otherwise it is a root. Pass parentGoalId yourself to put it under a specific Goal. Pass repo to anchor the work to a specific repository ("owner/repo" or repo name); delegated children inherit it. Pass title to name it in one to five words, as a person would refer to it out loud ("the night rings") \u2014 it heads every list and is spoken on a call; without one the brain writes it. Admission only:
|
|
10023
|
+
var CREATE_GOAL_DESCRIPTION = 'Create a durable Goal for an outcome. Without parentGoalId it is placed against your open Goals: if one already IS this work, that Goal comes back (existing: true) and nothing new is created \u2014 continue it; if the work belongs under one, it is created there (parentGoalId in the receipt); otherwise it is a root. Pass parentGoalId yourself to put it under a specific Goal. Pass repo to anchor the work to a specific repository ("owner/repo" or repo name); delegated children inherit it. Pass title to name it in one to five words, as a person would refer to it out loud ("the night rings") \u2014 it heads every list and is spoken on a call; without one the brain writes it. Admission only: claim it before doing work, then update it as it advances. Returns an admission receipt with goalId, current state, revision, ownerParticipant, and the next step; no Goal content or execution lease.';
|
|
10024
10024
|
var UpdateGoalSchema = external_exports.object({
|
|
10025
10025
|
revision: external_exports.number().int().positive(),
|
|
10026
10026
|
changes: external_exports.object({
|
|
@@ -10056,12 +10056,13 @@ var GetGoalSchema = external_exports.object({
|
|
|
10056
10056
|
* between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
|
|
10057
10057
|
diagnose: external_exports.boolean().optional()
|
|
10058
10058
|
}).strict();
|
|
10059
|
-
var GET_GOAL_DESCRIPTION = "Read one Goal without claiming it: its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), `next`, the one step to take, and `more`, where to read further. A question in that conversation reads `open` (nothing yet), `answered` (a choice was made, and `answer` carries it), `replied` (they said something and the read settled the question on their words \u2014 NO option of yours was chosen, and the words are the reply line beside it, so read that before you act), or `closed`. The conversation carries everything the person said, every open question and your newest entry; your older entries are left out and counted \u2014 history: true reads every entry in full.
|
|
10060
|
-
var UPDATE_GOAL_DESCRIPTION = `Update
|
|
10061
|
-
var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own
|
|
10059
|
+
var GET_GOAL_DESCRIPTION = "Read one Goal without claiming it, including others (the ten most recent other contributors, with names, latest entry headlines and times): its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), `next`, the one step to take, and `more`, where to read further. A question in that conversation reads `open` (nothing yet), `answered` (a choice was made, and `answer` carries it), `replied` (they said something and the read settled the question on their words \u2014 NO option of yours was chosen, and the words are the reply line beside it, so read that before you act), or `closed`. The conversation carries everything the person said, every open question and your newest entry; your older entries are left out and counted \u2014 history: true reads every entry in full. Every Goal of your person is readable, whichever of their agents owns it; another account's Goals are not disclosed.\n\ndiagnose: true is for when a read has CONFUSED you \u2014 not for the working loop. It adds `diagnosis`, which asks every reader that already answers a question about your work and NAMES the reader behind every value, so you never guess which of two places to look: what state your work is really in (the stored `goals.state` beside the derived `goal_execution_state`, which can differ); whether anything is armed to ring and when (`ladder_candidates`, with the ladder's own last decision); whether a wake fired for you and what it did (durable `wake.*` events, beside when your process was last heard from); and what has reached you (`list_waiting` \u2014 review flags, unstarted work, open questions, the person's newest words). Read `diagnosis.disagreements` FIRST: each one is two readers giving different values for the same fact, with both values and both sources, and it never chooses between them \u2014 that is yours to do, from the Goal's own history. `diagnosis.looked` names every reader asked, including any that could not answer, so an empty answer is never confused with a broken one.";
|
|
10060
|
+
var UPDATE_GOAL_DESCRIPTION = `Update a Goal you are on at an exact revision. You are on a Goal you own, or one you have written on (an update with progress, or a contact on it); any of your person's agents may write on any of their Goals, and one that never touched a Goal is refused (409 goal_not_joined). State, ownership, dependencies, children, progress, title, and review acknowledgement are explicit; stale revisions are rejected. A CONTACT ON THIS GOAL MOVES ITS REVISION: a question filed on a Goal is a change to it, so an update prepared before a contact and sent after it is refused as stale (409 goal_revision_conflict) \u2014 re-read the Goal, then write. title is the work's name in one to five words, as a person would refer to it out loud (it is spoken on a call and heads every list); null clears it. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. withdraw: [questionId] takes back a question YOU asked on this Goal that is still open \u2014 because you acted on it yourself, it no longer matters, or you asked it wrongly (e.g. waiting: hard when nothing was blocked): it is cancelled, not answered, its card closes and it stops ringing; the id is the one the conversation shows. state: done means the work was accomplished, and it can be reopened: when the person says it is not done ("it's not working"), set state: active on that same Goal instead of starting a new one; its parent reopens with it. state: done while children are still open records your part as done: the Goal waits and closes by itself when its last open child closes. cancelled is final. Returns the Goal as get_goal reads it, at its new revision.`;
|
|
10061
|
+
var CLAIM_GOAL_DESCRIPTION = "Claim a pending answer to your question or the oldest runnable or review-pending Goal you own. Pass goalId to join any Goal of your person; its assignment stays unchanged. Read others before overlapping another agent\u2019s work. Joining lets you contribute and change its state; use update_goal with ownerParticipant when the assignment itself should change. Returns the Goal as get_goal reads it, and marks you as on it, which never shuts another agent out: other agents of your person may write on it and change it too, and two changes at once are told apart by revision (409 goal_revision_conflict).";
|
|
10062
10062
|
var CHECK_REPLIES_DESCRIPTION = "What is waiting for you: your open Deliveries (a request the user started toward you, a handoff), one row each with its Goals, how many decisions are still open, and the newest words in brief; `assigned`, your Goals nobody has started yet, however they became yours (claim_goal({goalId}) starts one); and `review`, your Goals with something new on them \u2014 the user's answers and notes are Entries on the Goal, not Deliveries. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread a Delivery in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true clears the Goal from `review` and closes the Deliveries addressed to you on it.";
|
|
10063
10063
|
var CheckRepliesSchema = external_exports.object({}).strict();
|
|
10064
10064
|
var AGENT_TOOLS = [
|
|
10065
|
+
{ name: "who_is_working", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use get_goal to read a Goal and its other contributors before starting overlapping work. Read-only; no arguments.", inputSchema: mcpInputSchema(CheckRepliesSchema) },
|
|
10065
10066
|
{ name: "contact", description: CONTACT_DESCRIPTION, inputSchema: CONTACT_SCHEMA },
|
|
10066
10067
|
{ name: "check_replies", description: CHECK_REPLIES_DESCRIPTION, inputSchema: mcpInputSchema(CheckRepliesSchema) },
|
|
10067
10068
|
{ name: "create_goal", description: CREATE_GOAL_DESCRIPTION, inputSchema: mcpInputSchema(CreateGoalToolSchema) },
|
|
@@ -10070,8 +10071,158 @@ var AGENT_TOOLS = [
|
|
|
10070
10071
|
{ name: "update_goal", description: UPDATE_GOAL_DESCRIPTION, inputSchema: mcpInputSchema(UpdateGoalToolSchema) }
|
|
10071
10072
|
];
|
|
10072
10073
|
var AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
|
|
10074
|
+
var id = external_exports.string().uuid();
|
|
10075
|
+
var key = external_exports.string().min(1).max(40);
|
|
10076
|
+
var participant = external_exports.string().regex(/^(human|agent):.+$/, "a participant ID such as agent:<uuid>");
|
|
10077
|
+
var SourceSchema = external_exports.object({
|
|
10078
|
+
entryId: id,
|
|
10079
|
+
span: external_exports.object({ start: external_exports.number().int().min(0), end: external_exports.number().int().min(1) }).strict().refine((s) => s.end > s.start, "a span ends after it starts").optional()
|
|
10080
|
+
}).strict();
|
|
10081
|
+
var ResultRefSchema = external_exports.union([external_exports.object({ id }).strict(), external_exports.object({ local: key }).strict()]);
|
|
10082
|
+
var NodeRefSchema = external_exports.object({ type: external_exports.enum(["goal", "question"]), id }).strict();
|
|
10083
|
+
var DependencySchema = external_exports.object({
|
|
10084
|
+
blocker: NodeRefSchema,
|
|
10085
|
+
blocked: NodeRefSchema,
|
|
10086
|
+
action: external_exports.enum(["start", "complete", "answer"]),
|
|
10087
|
+
reason: external_exports.string().max(500).optional()
|
|
10088
|
+
}).strict();
|
|
10089
|
+
var goalState = external_exports.enum(["open", "completed", "canceled"]);
|
|
10090
|
+
var existingGoalChanges = [
|
|
10091
|
+
external_exports.object({ kind: external_exports.literal("edit"), goalId: id, title: external_exports.string().min(1).max(120).optional(), outcome: external_exports.string().min(1).optional() }).strict().refine((c) => c.title !== void 0 || c.outcome !== void 0, "an edit changes the title or the outcome"),
|
|
10092
|
+
external_exports.object({ kind: external_exports.literal("state"), goalId: id, state: goalState }).strict(),
|
|
10093
|
+
external_exports.object({ kind: external_exports.literal("assign"), goalId: id, ownerId: participant }).strict(),
|
|
10094
|
+
external_exports.object({ kind: external_exports.literal("defer"), goalId: id, until: external_exports.string().datetime().nullable() }).strict(),
|
|
10095
|
+
external_exports.object({ kind: external_exports.literal("move"), goalId: id, parentGoalId: id.nullable() }).strict(),
|
|
10096
|
+
external_exports.object({
|
|
10097
|
+
kind: external_exports.literal("dependency"),
|
|
10098
|
+
change: external_exports.enum(["add", "remove"]),
|
|
10099
|
+
goalId: id,
|
|
10100
|
+
dependsOnGoalId: id,
|
|
10101
|
+
action: external_exports.enum(["start", "complete"]),
|
|
10102
|
+
reason: external_exports.string().max(500).optional()
|
|
10103
|
+
}).strict()
|
|
10104
|
+
];
|
|
10105
|
+
var GoalChangeSchema = external_exports.union([
|
|
10106
|
+
external_exports.object({
|
|
10107
|
+
kind: external_exports.literal("create"),
|
|
10108
|
+
title: external_exports.string().min(1).max(120),
|
|
10109
|
+
outcome: external_exports.string().min(1),
|
|
10110
|
+
ownerId: participant,
|
|
10111
|
+
parentGoalId: id.optional(),
|
|
10112
|
+
sourceEntryIds: external_exports.array(id)
|
|
10113
|
+
}).strict(),
|
|
10114
|
+
...existingGoalChanges
|
|
10115
|
+
]);
|
|
10116
|
+
var BrainGoalChangeSchema = external_exports.union([
|
|
10117
|
+
// newSession: Paigy starts a session to own it (owner, 2026-10-06); until it has, the person owns it.
|
|
10118
|
+
external_exports.object({
|
|
10119
|
+
kind: external_exports.literal("create"),
|
|
10120
|
+
outcome: external_exports.string().min(1),
|
|
10121
|
+
title: external_exports.string().min(1).max(120),
|
|
10122
|
+
ownerId: participant,
|
|
10123
|
+
parentGoal: ResultRefSchema.nullable(),
|
|
10124
|
+
newSession: external_exports.literal(true).optional()
|
|
10125
|
+
}).strict(),
|
|
10126
|
+
...existingGoalChanges
|
|
10127
|
+
]);
|
|
10128
|
+
var option = external_exports.object({ id: external_exports.string().min(1).max(40), label: external_exports.string().min(1), hint: external_exports.string().optional() }).strict();
|
|
10129
|
+
var QuestionChangeSchema = external_exports.union([
|
|
10130
|
+
external_exports.object({
|
|
10131
|
+
kind: external_exports.literal("create"),
|
|
10132
|
+
text: external_exports.string().min(1),
|
|
10133
|
+
answererId: participant,
|
|
10134
|
+
goal: ResultRefSchema.optional(),
|
|
10135
|
+
blocks: external_exports.array(external_exports.object({ question: ResultRefSchema }).strict()),
|
|
10136
|
+
options: external_exports.array(option).min(2).optional(),
|
|
10137
|
+
pickMode: external_exports.enum(["one", "many", "rank"]).optional()
|
|
10138
|
+
}).strict().refine((c) => c.options === void 0 === (c.pickMode === void 0), "options and pickMode come together"),
|
|
10139
|
+
external_exports.object({ kind: external_exports.literal("edit"), questionId: id, text: external_exports.string().min(1) }).strict(),
|
|
10140
|
+
external_exports.object({ kind: external_exports.literal("assign"), questionId: id, answererId: participant }).strict(),
|
|
10141
|
+
external_exports.object({ kind: external_exports.literal("withdraw"), questionId: id }).strict(),
|
|
10142
|
+
external_exports.object({ kind: external_exports.literal("dependency"), change: external_exports.enum(["add", "remove"]), edge: DependencySchema }).strict()
|
|
10143
|
+
]);
|
|
10144
|
+
var lessonScope = external_exports.union([
|
|
10145
|
+
external_exports.object({ kind: external_exports.literal("user") }).strict(),
|
|
10146
|
+
external_exports.object({ kind: external_exports.literal("goal"), goal: ResultRefSchema }).strict()
|
|
10147
|
+
]);
|
|
10148
|
+
var LessonChangeSchema = external_exports.union([
|
|
10149
|
+
external_exports.object({ kind: external_exports.literal("remember"), text: external_exports.string().min(1), scope: lessonScope }).strict(),
|
|
10150
|
+
external_exports.object({ kind: external_exports.literal("revise"), lessonId: id, text: external_exports.string().min(1), scope: lessonScope }).strict(),
|
|
10151
|
+
external_exports.object({ kind: external_exports.literal("withdraw"), lessonId: id }).strict()
|
|
10152
|
+
]);
|
|
10153
|
+
var BrainNextSchema = external_exports.object({
|
|
10154
|
+
instructions: external_exports.array(external_exports.object({ entryKeys: external_exports.array(key).min(1) }).strict()),
|
|
10155
|
+
say: external_exports.array(key),
|
|
10156
|
+
then: external_exports.enum(["listen", "hold", "end", "none"]),
|
|
10157
|
+
waitFor: external_exports.array(ResultRefSchema),
|
|
10158
|
+
reason: external_exports.string()
|
|
10159
|
+
}).strict();
|
|
10160
|
+
var messageBase = {
|
|
10161
|
+
key,
|
|
10162
|
+
text: external_exports.string().min(1),
|
|
10163
|
+
goals: external_exports.array(ResultRefSchema),
|
|
10164
|
+
questions: external_exports.array(ResultRefSchema),
|
|
10165
|
+
entryKeys: external_exports.array(key),
|
|
10166
|
+
obligationIds: external_exports.array(id)
|
|
10167
|
+
};
|
|
10168
|
+
var BrainMessageSchema = external_exports.union([
|
|
10169
|
+
external_exports.object({ ...messageBase, to: external_exports.object({ kind: external_exports.literal("user") }).strict() }).strict(),
|
|
10170
|
+
external_exports.object({
|
|
10171
|
+
...messageBase,
|
|
10172
|
+
to: external_exports.object({ kind: external_exports.literal("session"), sessionId: id }).strict(),
|
|
10173
|
+
call: external_exports.object({ callId: id, listen: external_exports.literal(true) }).strict().optional()
|
|
10174
|
+
}).strict()
|
|
10175
|
+
]);
|
|
10176
|
+
var BrainSearchSchema = external_exports.object({
|
|
10177
|
+
query: external_exports.string().min(1).max(500),
|
|
10178
|
+
within: external_exports.array(external_exports.enum(["entries", "goals", "answers"])).min(1),
|
|
10179
|
+
goalId: id.optional()
|
|
10180
|
+
}).strict();
|
|
10181
|
+
var BrainResultSchema = external_exports.object({
|
|
10182
|
+
messages: external_exports.array(BrainMessageSchema),
|
|
10183
|
+
entries: external_exports.array(external_exports.object({ key, sources: external_exports.array(SourceSchema).min(1), goals: external_exports.array(ResultRefSchema) }).strict()),
|
|
10184
|
+
questions: external_exports.array(external_exports.object({ key, entryKeys: external_exports.array(key), change: QuestionChangeSchema }).strict()),
|
|
10185
|
+
answers: external_exports.array(external_exports.object({
|
|
10186
|
+
question: ResultRefSchema,
|
|
10187
|
+
entryKeys: external_exports.array(key).min(1),
|
|
10188
|
+
summary: external_exports.string().min(1),
|
|
10189
|
+
selectedOptionIds: external_exports.array(external_exports.string().min(1)).optional()
|
|
10190
|
+
}).strict()),
|
|
10191
|
+
goals: external_exports.array(external_exports.object({ key, entryKeys: external_exports.array(key), change: BrainGoalChangeSchema }).strict()),
|
|
10192
|
+
feedback: external_exports.array(external_exports.object({ entryKeys: external_exports.array(key).min(1) }).strict()),
|
|
10193
|
+
lessons: external_exports.array(external_exports.object({ entryKeys: external_exports.array(key).min(1), change: LessonChangeSchema }).strict()),
|
|
10194
|
+
next: BrainNextSchema,
|
|
10195
|
+
search: BrainSearchSchema.optional()
|
|
10196
|
+
}).strict();
|
|
10197
|
+
var str = external_exports.string();
|
|
10198
|
+
var strs = external_exports.array(external_exports.string());
|
|
10199
|
+
var CompactResultSchema = external_exports.object({
|
|
10200
|
+
next: external_exports.object({ say: strs, then: external_exports.enum(["listen", "hold", "end", "none"]), waitFor: strs, reason: str, instructions: strs }).strict(),
|
|
10201
|
+
messages: external_exports.array(external_exports.object({ key: str, to: str, text: str, about: strs, entries: strs, owed: strs, inviteToCall: external_exports.boolean() }).strict()),
|
|
10202
|
+
entries: external_exports.array(external_exports.object({ key: str, from: str, start: external_exports.number().int(), end: external_exports.number().int(), on: strs }).strict()),
|
|
10203
|
+
answers: external_exports.array(external_exports.object({ question: str, entries: strs, summary: str, picked: strs }).strict()),
|
|
10204
|
+
changes: external_exports.array(external_exports.object({
|
|
10205
|
+
key: str,
|
|
10206
|
+
what: external_exports.enum(["goal", "question", "lesson"]),
|
|
10207
|
+
op: external_exports.enum(["create", "edit", "assign", "open", "complete", "cancel", "defer", "move", "block", "unblock", "withdraw", "remember", "revise"]),
|
|
10208
|
+
id: str,
|
|
10209
|
+
text: str,
|
|
10210
|
+
title: str,
|
|
10211
|
+
who: str,
|
|
10212
|
+
under: str,
|
|
10213
|
+
gate: external_exports.enum(["start", "complete", "answer", "none"]),
|
|
10214
|
+
options: external_exports.array(external_exports.object({ id: str, label: str }).strict()),
|
|
10215
|
+
pick: external_exports.enum(["one", "many", "rank", "words"]),
|
|
10216
|
+
entries: strs
|
|
10217
|
+
}).strict()),
|
|
10218
|
+
feedback: external_exports.array(strs),
|
|
10219
|
+
search: external_exports.object({ query: str, within: strs, goal: str }).strict()
|
|
10220
|
+
}).strict();
|
|
10221
|
+
var COMPACT_RESULT_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10222
|
+
zodToJsonSchema(CompactResultSchema, { $refStrategy: "none" })
|
|
10223
|
+
);
|
|
10073
10224
|
var LIVE_MS = 3 * 6e4;
|
|
10074
|
-
var WORKING_MS =
|
|
10225
|
+
var WORKING_MS = 60 * 6e4;
|
|
10075
10226
|
var ContextSchema = external_exports.object({
|
|
10076
10227
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
10077
10228
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -10289,7 +10440,7 @@ var IntentSchema = external_exports.object({
|
|
|
10289
10440
|
* (`parseDelay`, #1292), and a value here simply wins over that reading. */
|
|
10290
10441
|
dueInSeconds: external_exports.number().int().positive().optional(),
|
|
10291
10442
|
/** Feedback only (#812): WHICH failure the complaint names — typed by the mapper that
|
|
10292
|
-
* already read the utterance, so `
|
|
10443
|
+
* already read the utterance, so `signals.kind` stops defaulting to
|
|
10293
10444
|
* 'other' on every row. A table that records that something was wrong and nothing
|
|
10294
10445
|
* about what cannot answer "is the bot looping less this week?". */
|
|
10295
10446
|
fault: external_exports.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
|
|
@@ -10423,7 +10574,13 @@ var AgendaTurnSchema = external_exports.object({
|
|
|
10423
10574
|
/** Whether the walk WAITS for an answer before moving on. Absent = derived as today
|
|
10424
10575
|
* (a question blocks, context flows). blocking:false on a question = ask and move
|
|
10425
10576
|
* on, the claim stays pending; blocking:true on context = hold for a reply. */
|
|
10426
|
-
blocking: external_exports.boolean().optional()
|
|
10577
|
+
blocking: external_exports.boolean().optional(),
|
|
10578
|
+
/** SPOKEN ONLY IF THEY SAY NOTHING (owner, 2026-10-01, call 812de935: "you're gonna re-ask, but it
|
|
10579
|
+
* shouldn't be the same words … more like, hey, are you still there, or are you able to answer, or
|
|
10580
|
+
* would you need more information"). The walk holds this turn out of its queue; at the queue's end it
|
|
10581
|
+
* listens for the last word, and only if that listen is silent is this turn said and asked. If they
|
|
10582
|
+
* speak, it is dropped and their words are taken like any reply. */
|
|
10583
|
+
ifSilent: external_exports.boolean().optional()
|
|
10427
10584
|
});
|
|
10428
10585
|
var CLAIM_STALE_MS = 30 * 6e4;
|
|
10429
10586
|
var InboxItemSchema = external_exports.object({
|
|
@@ -10567,7 +10724,18 @@ var InboxItemSchema = external_exports.object({
|
|
|
10567
10724
|
/** THAT GOAL'S NAME (#2416) — what Activity's row is headed by, since a row there is one Goal
|
|
10568
10725
|
* and the cards it holds sit behind it. Stamped by the same read as `goalState`. */
|
|
10569
10726
|
goalTitle: external_exports.string().optional()
|
|
10570
|
-
}).optional()
|
|
10727
|
+
}).optional(),
|
|
10728
|
+
/** WHAT THIS CARD IS, IN TWELVE CHARACTERS (#3019) — the hash of every other field on it, stamped
|
|
10729
|
+
* by the one read that serves the open list (`apps/api/src/inbox/project.ts` `inboxFor`). It is
|
|
10730
|
+
* how the incremental read knows a card has not moved: the phone echoes back the revs it holds
|
|
10731
|
+
* (`POST /api/inbox/changes`) and is sent only the cards whose rev differs.
|
|
10732
|
+
*
|
|
10733
|
+
* THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK — the same mechanism as `QueueItem.rev`
|
|
10734
|
+
* (#2928) and an entry's (#3018), for the same reason: a card shows facts no `updated_at` of its
|
|
10735
|
+
* own moves (its Goal's state and name, how cold the work behind it has gone, whether a ring is
|
|
10736
|
+
* live). Optional, so a fixture, the demo and the archive lens need not spell one, and a card
|
|
10737
|
+
* with no rev is simply always re-sent. */
|
|
10738
|
+
rev: external_exports.string().optional()
|
|
10571
10739
|
});
|
|
10572
10740
|
var APNS_TOKEN_RE = /^[0-9a-fA-F]{64}$/;
|
|
10573
10741
|
var PushTokenSchema = external_exports.object({
|
|
@@ -10939,7 +11107,22 @@ var QueueItemSchema = external_exports.object({
|
|
|
10939
11107
|
* minute after the person speaks erases their instant from it, and the durable traces the client
|
|
10940
11108
|
* can see (`replies`, `questions[].answeredAt`) miss a spontaneous note entirely — a `request`
|
|
10941
11109
|
* Entry with no `about_id` is in neither. */
|
|
10942
|
-
lastPersonAt: external_exports.string().nullable().optional()
|
|
11110
|
+
lastPersonAt: external_exports.string().nullable().optional(),
|
|
11111
|
+
/** WHAT THIS ROW IS, IN TWELVE CHARACTERS (#2928) — the hash of every other field on it, stamped
|
|
11112
|
+
* by the one projection that builds the row (`apps/api/src/goal/queue.ts`). It is how the
|
|
11113
|
+
* incremental read knows a row has not moved: the phone echoes back the revs it holds
|
|
11114
|
+
* (`POST /api/goals/changes`) and is sent only the rows whose rev differs.
|
|
11115
|
+
*
|
|
11116
|
+
* IT IS THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK. Nothing here reasons about which
|
|
11117
|
+
* writes change which field — the comparison is over the bytes the phone is holding, so a fact
|
|
11118
|
+
* the row shows that no `updated_at` moves for (a lease lapsing, a dependency's state, a
|
|
11119
|
+
* sibling appearing in `childGoalIds`) cannot go unnoticed. Optional because a hand-built
|
|
11120
|
+
* queue (a fixture, the demo) spells none, and a row with no rev is simply always re-sent. */
|
|
11121
|
+
rev: external_exports.string().optional()
|
|
11122
|
+
});
|
|
11123
|
+
var QueueDeltaSchema = external_exports.object({
|
|
11124
|
+
ids: external_exports.array(external_exports.string()),
|
|
11125
|
+
items: external_exports.array(QueueItemSchema)
|
|
10943
11126
|
});
|
|
10944
11127
|
var COLD_AFTER_MS = 3 * 24 * 60 * 60 * 1e3;
|
|
10945
11128
|
var NoteSourceSchema = external_exports.enum(["app", "call"]);
|
|
@@ -11112,6 +11295,11 @@ var DeviceTokenSchema = external_exports.object({
|
|
|
11112
11295
|
/** Local host recovery must preserve the launch's runtime and Paigy identity. */
|
|
11113
11296
|
harness: external_exports.enum(["claude", "codex", "agy"]).optional(),
|
|
11114
11297
|
session_id: external_exports.string().uuid().optional(),
|
|
11298
|
+
/** A conversation the host must not resume: its context is full, so every turn fails
|
|
11299
|
+
* ("Prompt is too long"). Written when a run hits it (`run.ts` `onFull`); the host skips a slot
|
|
11300
|
+
* whose resumable session is this one, so its Goals reach the dead-agent handoff instead of a
|
|
11301
|
+
* copy that types the person's words into a turn that cannot run (Calls, 2026-10-06). */
|
|
11302
|
+
full_session: external_exports.string().optional(),
|
|
11115
11303
|
uik_pub: external_exports.string().nullable().optional()
|
|
11116
11304
|
});
|
|
11117
11305
|
var SupportRequestSchema = external_exports.object({
|
|
@@ -11257,8 +11445,8 @@ var CallRecapSchema = external_exports.object({
|
|
|
11257
11445
|
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
|
|
11258
11446
|
});
|
|
11259
11447
|
function sessionSlot(sessionId2) {
|
|
11260
|
-
const
|
|
11261
|
-
return `session:${
|
|
11448
|
+
const id22 = sessionId2 ?? sessionId();
|
|
11449
|
+
return `session:${id22.slice(0, 8)}`;
|
|
11262
11450
|
}
|
|
11263
11451
|
function agentName() {
|
|
11264
11452
|
return process.env.PAIGY_AGENT || sessionSlot();
|
|
@@ -11315,7 +11503,7 @@ function updateSlot(agent2, patch) {
|
|
|
11315
11503
|
}
|
|
11316
11504
|
function slotIdentity(agent2) {
|
|
11317
11505
|
const t = readTokenFile()[agent2];
|
|
11318
|
-
return { name: t?.name ?? null, voice: t?.voice ?? null, tokenId: t?.token_id ?? null, workspace: t?.workspace ?? null, harness: t?.harness ?? null, sessionId: t?.session_id ?? null };
|
|
11506
|
+
return { name: t?.name ?? null, voice: t?.voice ?? null, tokenId: t?.token_id ?? null, workspace: t?.workspace ?? null, harness: t?.harness ?? null, sessionId: t?.session_id ?? null, fullSession: t?.full_session ?? null };
|
|
11319
11507
|
}
|
|
11320
11508
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
11321
11509
|
function readToken(agent2 = agentName()) {
|
|
@@ -11477,7 +11665,9 @@ async function recordDecision(decision, opts = {}) {
|
|
|
11477
11665
|
async function heartbeat(runtime, opts = {}) {
|
|
11478
11666
|
const body = {
|
|
11479
11667
|
...runtime !== void 0 ? { runtime } : {},
|
|
11480
|
-
...opts.activity !== void 0 ? { activity: opts.activity } : {}
|
|
11668
|
+
...opts.activity !== void 0 ? { activity: opts.activity } : {},
|
|
11669
|
+
// A listener's beat (`paigy-listen`): stamps when it last swept, and is not presence.
|
|
11670
|
+
...opts.listening ? { listening: true } : {}
|
|
11481
11671
|
};
|
|
11482
11672
|
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/presence`, {
|
|
11483
11673
|
method: "POST",
|
|
@@ -11569,6 +11759,7 @@ function currentRepo(cwd = process.cwd()) {
|
|
|
11569
11759
|
return asked;
|
|
11570
11760
|
}
|
|
11571
11761
|
var NOT_SENT = "Recorded as progress, not sent: nobody was notified. If the person should see it (a result, something to try or check, a finding), contact again and say so; report progress with update_goal.";
|
|
11762
|
+
var DEMOTED = "Delivered as a card, not a call: the person's settings do not allow a call right now (silent, quiet hours, or a mode). It has reached them; do not send it again, with or without a Goal.";
|
|
11572
11763
|
async function contact(input, opts = {}) {
|
|
11573
11764
|
const parsed = ContactSchema.parse(input);
|
|
11574
11765
|
opts.signal?.throwIfAborted();
|
|
@@ -11577,6 +11768,7 @@ async function contact(input, opts = {}) {
|
|
|
11577
11768
|
let deliveryId;
|
|
11578
11769
|
let deliveries = [];
|
|
11579
11770
|
let recorded = [];
|
|
11771
|
+
let demoted;
|
|
11580
11772
|
if ("deliveryId" in parsed) {
|
|
11581
11773
|
deliveryId = parsed.deliveryId;
|
|
11582
11774
|
} else {
|
|
@@ -11607,13 +11799,14 @@ async function contact(input, opts = {}) {
|
|
|
11607
11799
|
if (!res.ok) await fail("batch contact", res);
|
|
11608
11800
|
const receipt = await res.json();
|
|
11609
11801
|
deliveries = receipt.deliveries ?? [];
|
|
11802
|
+
if (parsed.channel === "call" && receipt.channel && receipt.channel !== "call") demoted = DEMOTED;
|
|
11610
11803
|
recorded = receipt.recorded ?? [];
|
|
11611
11804
|
if (deliveries.length === 0 && recorded.length) return { sent: false, recorded, message: NOT_SENT };
|
|
11612
11805
|
if (deliveries.length === 0) throw new Error("Goal contact returned no Delivery identities");
|
|
11613
11806
|
deliveryId = deliveries[0]?.deliveryId ?? "";
|
|
11614
11807
|
if (!deliveryId) throw new Error("Goal contact returned no valid Delivery identity");
|
|
11615
11808
|
}
|
|
11616
|
-
const all = deliveries.filter((d) => !!d.deliveryId && !!d.goalId).map(({ id, deliveryId: deliveryId2, goalId }) => ({ ...
|
|
11809
|
+
const all = deliveries.filter((d) => !!d.deliveryId && !!d.goalId).map(({ id: id22, deliveryId: deliveryId2, goalId }) => ({ ...id22 ? { id: id22 } : {}, deliveryId: deliveryId2, goalId }));
|
|
11617
11810
|
const joinedCard = deliveries[0]?.joinedCard === true;
|
|
11618
11811
|
const joinedCall = deliveries[0]?.joinedCall === true;
|
|
11619
11812
|
const read = async (signal2) => {
|
|
@@ -11625,7 +11818,8 @@ async function contact(input, opts = {}) {
|
|
|
11625
11818
|
...all.length > 1 ? { deliveries: all } : {},
|
|
11626
11819
|
...recorded.length ? { recorded, notSent: NOT_SENT } : {},
|
|
11627
11820
|
...joinedCard ? { joinedCard: true } : {},
|
|
11628
|
-
...joinedCall ? { joinedCall: true } : {}
|
|
11821
|
+
...joinedCall ? { joinedCall: true } : {},
|
|
11822
|
+
...demoted ? { demoted } : {}
|
|
11629
11823
|
};
|
|
11630
11824
|
};
|
|
11631
11825
|
const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
|
|
@@ -11762,9 +11956,9 @@ function connect(line) {
|
|
|
11762
11956
|
ws.addEventListener("error", again);
|
|
11763
11957
|
}
|
|
11764
11958
|
function subscribeRealtime(args) {
|
|
11765
|
-
const
|
|
11766
|
-
let line = lines.get(
|
|
11767
|
-
if (!line) lines.set(
|
|
11959
|
+
const key22 = `${args.url} ${args.anonKey}`;
|
|
11960
|
+
let line = lines.get(key22);
|
|
11961
|
+
if (!line) lines.set(key22, line = { url: args.url, anonKey: args.anonKey, open: false, attempt: 0, ref: 0, topics: /* @__PURE__ */ new Map() });
|
|
11768
11962
|
let t = line.topics.get(args.topic);
|
|
11769
11963
|
if (!t) {
|
|
11770
11964
|
line.topics.set(args.topic, t = { listeners: /* @__PURE__ */ new Set(), joined: false, attempt: 0 });
|
|
@@ -11786,7 +11980,7 @@ function subscribeRealtime(args) {
|
|
|
11786
11980
|
if (line.open) frame(line, `realtime:${args.topic}`, "phx_leave", {});
|
|
11787
11981
|
return;
|
|
11788
11982
|
}
|
|
11789
|
-
lines.delete(
|
|
11983
|
+
lines.delete(key22);
|
|
11790
11984
|
stop(line);
|
|
11791
11985
|
try {
|
|
11792
11986
|
line.ws?.close();
|
|
@@ -11914,9 +12108,9 @@ function detectAll(deps = {}) {
|
|
|
11914
12108
|
}
|
|
11915
12109
|
|
|
11916
12110
|
// src/host.ts
|
|
11917
|
-
import { readFileSync as
|
|
12111
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
11918
12112
|
import { homedir as homedir10 } from "os";
|
|
11919
|
-
import { join as
|
|
12113
|
+
import { join as join11 } from "path";
|
|
11920
12114
|
|
|
11921
12115
|
// src/triage/evidence.ts
|
|
11922
12116
|
var OPEN_STATUSES = ["open", "assigned", "in_progress"];
|
|
@@ -12235,16 +12429,16 @@ function parseVerdicts(raw, knownIds, knownAgents) {
|
|
|
12235
12429
|
const seen = /* @__PURE__ */ new Set();
|
|
12236
12430
|
for (const row of rows) {
|
|
12237
12431
|
const r = row;
|
|
12238
|
-
const
|
|
12239
|
-
if (!
|
|
12432
|
+
const id3 = typeof r?.id === "string" ? r.id : "";
|
|
12433
|
+
if (!id3) {
|
|
12240
12434
|
dropped.push({ row, reason: "no id" });
|
|
12241
12435
|
continue;
|
|
12242
12436
|
}
|
|
12243
|
-
if (!knownIds.has(
|
|
12437
|
+
if (!knownIds.has(id3)) {
|
|
12244
12438
|
dropped.push({ row, reason: "id is not a note in this queue" });
|
|
12245
12439
|
continue;
|
|
12246
12440
|
}
|
|
12247
|
-
if (seen.has(
|
|
12441
|
+
if (seen.has(id3)) {
|
|
12248
12442
|
dropped.push({ row, reason: "duplicate verdict for this note" });
|
|
12249
12443
|
continue;
|
|
12250
12444
|
}
|
|
@@ -12259,12 +12453,12 @@ function parseVerdicts(raw, knownIds, knownAgents) {
|
|
|
12259
12453
|
continue;
|
|
12260
12454
|
}
|
|
12261
12455
|
verdicts.push({
|
|
12262
|
-
id,
|
|
12456
|
+
id: id3,
|
|
12263
12457
|
verdict,
|
|
12264
12458
|
agent: verdict === "assign" ? agent2 : null,
|
|
12265
12459
|
why: typeof r?.why === "string" ? r.why.trim().slice(0, 200) : null
|
|
12266
12460
|
});
|
|
12267
|
-
seen.add(
|
|
12461
|
+
seen.add(id3);
|
|
12268
12462
|
}
|
|
12269
12463
|
return { ok: true, verdicts, dropped };
|
|
12270
12464
|
}
|
|
@@ -12419,7 +12613,7 @@ async function runTriage(opts, deps = {}) {
|
|
|
12419
12613
|
const acted = suggested(run);
|
|
12420
12614
|
log2(acted === 0 ? "Nothing to suggest \u2014 sent, so your phone shows the queue as clean." : `Sent to your phone \u2014 accept it there (${plural(acted, "suggestion")}).`);
|
|
12421
12615
|
if (!opts.apply) return 0;
|
|
12422
|
-
const accept = deps.accept ?? ((
|
|
12616
|
+
const accept = deps.accept ?? ((id3, body) => acceptTriage(id3, body));
|
|
12423
12617
|
const groups = [
|
|
12424
12618
|
{ body: { group: "close" }, label: `close ${plural(run.close.length, "note")}`, count: run.close.length, preapproved: opts.yes.close },
|
|
12425
12619
|
{ body: { group: "stale" }, label: `close ${plural(run.stale.length, "stale note")}`, count: run.stale.length, preapproved: opts.yes.stale },
|
|
@@ -12842,19 +13036,19 @@ var getRefs2 = (options) => {
|
|
|
12842
13036
|
};
|
|
12843
13037
|
|
|
12844
13038
|
// ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/errorMessages.js
|
|
12845
|
-
function addErrorMessage2(res,
|
|
13039
|
+
function addErrorMessage2(res, key3, errorMessage, refs) {
|
|
12846
13040
|
if (!refs?.errorMessages)
|
|
12847
13041
|
return;
|
|
12848
13042
|
if (errorMessage) {
|
|
12849
13043
|
res.errorMessage = {
|
|
12850
13044
|
...res.errorMessage,
|
|
12851
|
-
[
|
|
13045
|
+
[key3]: errorMessage
|
|
12852
13046
|
};
|
|
12853
13047
|
}
|
|
12854
13048
|
}
|
|
12855
|
-
function setResponseValueAndErrors2(res,
|
|
12856
|
-
res[
|
|
12857
|
-
addErrorMessage2(res,
|
|
13049
|
+
function setResponseValueAndErrors2(res, key3, value, errorMessage, refs) {
|
|
13050
|
+
res[key3] = value;
|
|
13051
|
+
addErrorMessage2(res, key3, errorMessage, refs);
|
|
12858
13052
|
}
|
|
12859
13053
|
|
|
12860
13054
|
// ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
|
|
@@ -13446,11 +13640,11 @@ function parseRecordDef2(def, refs) {
|
|
|
13446
13640
|
return {
|
|
13447
13641
|
type: "object",
|
|
13448
13642
|
required: def.keyType._def.values,
|
|
13449
|
-
properties: def.keyType._def.values.reduce((acc,
|
|
13643
|
+
properties: def.keyType._def.values.reduce((acc, key3) => ({
|
|
13450
13644
|
...acc,
|
|
13451
|
-
[
|
|
13645
|
+
[key3]: parseDef2(def.valueType._def, {
|
|
13452
13646
|
...refs,
|
|
13453
|
-
currentPath: [...refs.currentPath, "properties",
|
|
13647
|
+
currentPath: [...refs.currentPath, "properties", key3]
|
|
13454
13648
|
}) ?? parseAnyDef2(refs)
|
|
13455
13649
|
}), {}),
|
|
13456
13650
|
additionalProperties: refs.rejectedAdditionalProperties
|
|
@@ -13517,10 +13711,10 @@ function parseMapDef2(def, refs) {
|
|
|
13517
13711
|
// ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js
|
|
13518
13712
|
function parseNativeEnumDef2(def) {
|
|
13519
13713
|
const object = def.values;
|
|
13520
|
-
const actualKeys = Object.keys(def.values).filter((
|
|
13521
|
-
return typeof object[object[
|
|
13714
|
+
const actualKeys = Object.keys(def.values).filter((key3) => {
|
|
13715
|
+
return typeof object[object[key3]] !== "number";
|
|
13522
13716
|
});
|
|
13523
|
-
const actualValues = actualKeys.map((
|
|
13717
|
+
const actualValues = actualKeys.map((key3) => object[key3]);
|
|
13524
13718
|
const parsedTypes = Array.from(new Set(actualValues.map((values) => typeof values)));
|
|
13525
13719
|
return {
|
|
13526
13720
|
type: parsedTypes.length === 1 ? parsedTypes[0] === "string" ? "string" : "number" : ["string", "number"],
|
|
@@ -14121,34 +14315,31 @@ function mcpInputSchema2(s) {
|
|
|
14121
14315
|
}
|
|
14122
14316
|
var AskInputSchema2 = external_exports.object({
|
|
14123
14317
|
id: external_exports.string().optional().describe("Optional idempotency key or client-side ID for this specific ask."),
|
|
14124
|
-
parentId: external_exports.string().uuid().optional().describe("The Goal this question is about \u2014 usually the one you are working on. The question goes onto that Goal and its answer comes back there. Omit it and
|
|
14318
|
+
parentId: external_exports.string().uuid().optional().describe("The Goal this question is about \u2014 usually the one you are working on. The question goes onto that Goal and its answer comes back there. Omit it and the question starts a new Goal of yours."),
|
|
14125
14319
|
repo: external_exports.string().optional().describe("Optional repository context."),
|
|
14126
14320
|
ask: external_exports.string().trim().min(1).max(1e4).describe(
|
|
14127
|
-
"ONE question, and only what is needed to answer it. Several questions are several asks in the array, one each: an answer settles the one ask it was given in the shape that ask declares, so a person who answers the part of a bundled ask that interested them settles nothing and is asked again. Paigy
|
|
14321
|
+
"ONE question, and only what is needed to answer it. Several questions are several asks in the array, one each: an answer settles the one ask it was given in the shape that ask declares, so a person who answers the part of a bundled ask that interested them settles nothing and is asked again. Paigy sends each ask exactly as you wrote it: a bundled ask arrives as one card. News, progress and findings are their own contact; ANY contact for a person already on a call JOINS that call, whatever channel you asked for, so several arrive as one call."
|
|
14128
14322
|
),
|
|
14129
14323
|
options: external_exports.array(OptionInputSchema2).min(1).max(6).optional(),
|
|
14130
|
-
//
|
|
14131
|
-
//
|
|
14132
|
-
//
|
|
14133
|
-
//
|
|
14134
|
-
// as "All three / 1 and 3 only / Just log it", which the person could not read without the text and
|
|
14135
|
-
// could not answer as the ticks he wanted. "many" is the one shape an agent knows and the read does
|
|
14136
|
-
// not: whether its own options exclude each other.
|
|
14324
|
+
// HOW THE OPTIONS ARE ANSWERED, SAID BY THE AGENT (owner, 2026-10-02: "one and many makes sense";
|
|
14325
|
+
// brain_prompts.md §1: structured agent questions need no model). Every card was pick-one while no
|
|
14326
|
+
// agent sent this; no read reshapes cards any more, so this is the card's shape, and the
|
|
14327
|
+
// description says so plainly.
|
|
14137
14328
|
select: external_exports.enum(["one", "many"]).optional().describe(
|
|
14138
|
-
'How the options are answered: "
|
|
14329
|
+
'How the options are answered: "many" makes a checklist (they may want several, e.g. independent fixes), "one" (the default) a pick (choosing one rules out the others). Send "many" whenever more than one option could be wanted at once. The person can always answer in their own words as well. Ignored without options.'
|
|
14139
14330
|
),
|
|
14140
14331
|
answers: external_exports.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/).optional().describe(
|
|
14141
14332
|
"Your reply answers a question the person asked you: the id the conversation shows for it (8 characters or whole). Their question closes with this reply as its answer, and any decision of yours it was holding back goes back to them. Needs parentId: the Goal the question is on. The reply is sent to them as it is; to also ask something new, send that as its own ask."
|
|
14142
14333
|
)
|
|
14143
14334
|
}).strict();
|
|
14144
14335
|
var StartContactSchema2 = external_exports.object({
|
|
14145
|
-
asks: external_exports.array(AskInputSchema2).min(1).describe("The questions to pose, one per object. An ask with a parentId is filed on that Goal as it is;
|
|
14336
|
+
asks: external_exports.array(AskInputSchema2).min(1).describe("The questions to pose, one per object. An ask with a parentId is filed on that Goal as it is; an ask naming no Goal starts a new Goal of yours."),
|
|
14146
14337
|
waiting: external_exports.enum(["none", "hard"]).default("none").describe("hard requests a call even when channel is notification; user permissions and ring cooldowns still apply."),
|
|
14147
14338
|
channel: external_exports.enum(["notification", "call"]).default("notification")
|
|
14148
14339
|
}).strict();
|
|
14149
14340
|
var ContactSchema2 = external_exports.union([StartContactSchema2, external_exports.object({ deliveryId: external_exports.string().uuid() }).strict()]);
|
|
14150
14341
|
var CONTACT_SCHEMA2 = { type: "object", ...mcpInputSchema2(ContactSchema2) };
|
|
14151
|
-
var CONTACT_DESCRIPTION2 = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'select', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none
|
|
14342
|
+
var CONTACT_DESCRIPTION2 = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'select', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none starts a new Goal of yours. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. One question per 'ask'; several questions are several objects in 'asks'. Each ask is sent exactly as written, with no reading in between: a bundled ask arrives as one card, so separate questions are separate asks. An ask with no options that is not blocking is a report, and a report is an UPDATE, never a claim on them: it reaches them and is listed apart from what waits on them, so no answer is owed and none should be awaited. On a Goal whose report card is still open it is added to that card, with no new push; Where your work stands while under way is progress: report it with update_goal, not contact. Send options (or waiting:'hard') when you actually need an answer. Each option's label must stand on its own \u2014 the person may see only the labels \u2014 so never a label that points into your text ('All three', 'Option 2', '1 and 3 only'). select:'many' makes the card a checklist, for independent options they may want several of; select:'one' (the default) a pick, for alternatives. The person can always answer in their own words, so never add an 'Other' option. If the person is already on a call, your contact joins that call automatically \u2014 whatever channel you asked for, with no ring \u2014 and the Delivery it returns IS that call: reread it with contact({deliveryId}) to see everything answered on it so far, and contact again while it is live to add information or a further question to the same call.";
|
|
14152
14343
|
var CreateGoalSchema2 = external_exports.object({
|
|
14153
14344
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
14154
14345
|
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
@@ -14171,7 +14362,7 @@ var CreateGoalSchema2 = external_exports.object({
|
|
|
14171
14362
|
var CreateGoalToolSchema2 = CreateGoalSchema2.extend({
|
|
14172
14363
|
idempotencyKey: CreateGoalSchema2.shape.idempotencyKey.optional().describe("Optional. One is minted per call; pass your own only so a retry lands on the same Goal.")
|
|
14173
14364
|
}).strict();
|
|
14174
|
-
var CREATE_GOAL_DESCRIPTION2 = 'Create a durable Goal for an outcome. Without parentGoalId it is placed against your open Goals: if one already IS this work, that Goal comes back (existing: true) and nothing new is created \u2014 continue it; if the work belongs under one, it is created there (parentGoalId in the receipt); otherwise it is a root. Pass parentGoalId yourself to put it under a specific Goal. Pass repo to anchor the work to a specific repository ("owner/repo" or repo name); delegated children inherit it. Pass title to name it in one to five words, as a person would refer to it out loud ("the night rings") \u2014 it heads every list and is spoken on a call; without one the brain writes it. Admission only:
|
|
14365
|
+
var CREATE_GOAL_DESCRIPTION2 = 'Create a durable Goal for an outcome. Without parentGoalId it is placed against your open Goals: if one already IS this work, that Goal comes back (existing: true) and nothing new is created \u2014 continue it; if the work belongs under one, it is created there (parentGoalId in the receipt); otherwise it is a root. Pass parentGoalId yourself to put it under a specific Goal. Pass repo to anchor the work to a specific repository ("owner/repo" or repo name); delegated children inherit it. Pass title to name it in one to five words, as a person would refer to it out loud ("the night rings") \u2014 it heads every list and is spoken on a call; without one the brain writes it. Admission only: claim it before doing work, then update it as it advances. Returns an admission receipt with goalId, current state, revision, ownerParticipant, and the next step; no Goal content or execution lease.';
|
|
14175
14366
|
var UpdateGoalSchema2 = external_exports.object({
|
|
14176
14367
|
revision: external_exports.number().int().positive(),
|
|
14177
14368
|
changes: external_exports.object({
|
|
@@ -14207,12 +14398,13 @@ var GetGoalSchema2 = external_exports.object({
|
|
|
14207
14398
|
* between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
|
|
14208
14399
|
diagnose: external_exports.boolean().optional()
|
|
14209
14400
|
}).strict();
|
|
14210
|
-
var GET_GOAL_DESCRIPTION2 = "Read one Goal without claiming it: its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), `next`, the one step to take, and `more`, where to read further. A question in that conversation reads `open` (nothing yet), `answered` (a choice was made, and `answer` carries it), `replied` (they said something and the read settled the question on their words \u2014 NO option of yours was chosen, and the words are the reply line beside it, so read that before you act), or `closed`. The conversation carries everything the person said, every open question and your newest entry; your older entries are left out and counted \u2014 history: true reads every entry in full.
|
|
14211
|
-
var UPDATE_GOAL_DESCRIPTION2 = `Update
|
|
14212
|
-
var CLAIM_GOAL_DESCRIPTION2 = "Claim the oldest runnable or review-pending Goal you own
|
|
14401
|
+
var GET_GOAL_DESCRIPTION2 = "Read one Goal without claiming it, including others (the ten most recent other contributors, with names, latest entry headlines and times): its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), `next`, the one step to take, and `more`, where to read further. A question in that conversation reads `open` (nothing yet), `answered` (a choice was made, and `answer` carries it), `replied` (they said something and the read settled the question on their words \u2014 NO option of yours was chosen, and the words are the reply line beside it, so read that before you act), or `closed`. The conversation carries everything the person said, every open question and your newest entry; your older entries are left out and counted \u2014 history: true reads every entry in full. Every Goal of your person is readable, whichever of their agents owns it; another account's Goals are not disclosed.\n\ndiagnose: true is for when a read has CONFUSED you \u2014 not for the working loop. It adds `diagnosis`, which asks every reader that already answers a question about your work and NAMES the reader behind every value, so you never guess which of two places to look: what state your work is really in (the stored `goals.state` beside the derived `goal_execution_state`, which can differ); whether anything is armed to ring and when (`ladder_candidates`, with the ladder's own last decision); whether a wake fired for you and what it did (durable `wake.*` events, beside when your process was last heard from); and what has reached you (`list_waiting` \u2014 review flags, unstarted work, open questions, the person's newest words). Read `diagnosis.disagreements` FIRST: each one is two readers giving different values for the same fact, with both values and both sources, and it never chooses between them \u2014 that is yours to do, from the Goal's own history. `diagnosis.looked` names every reader asked, including any that could not answer, so an empty answer is never confused with a broken one.";
|
|
14402
|
+
var UPDATE_GOAL_DESCRIPTION2 = `Update a Goal you are on at an exact revision. You are on a Goal you own, or one you have written on (an update with progress, or a contact on it); any of your person's agents may write on any of their Goals, and one that never touched a Goal is refused (409 goal_not_joined). State, ownership, dependencies, children, progress, title, and review acknowledgement are explicit; stale revisions are rejected. A CONTACT ON THIS GOAL MOVES ITS REVISION: a question filed on a Goal is a change to it, so an update prepared before a contact and sent after it is refused as stale (409 goal_revision_conflict) \u2014 re-read the Goal, then write. title is the work's name in one to five words, as a person would refer to it out loud (it is spoken on a call and heads every list); null clears it. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. withdraw: [questionId] takes back a question YOU asked on this Goal that is still open \u2014 because you acted on it yourself, it no longer matters, or you asked it wrongly (e.g. waiting: hard when nothing was blocked): it is cancelled, not answered, its card closes and it stops ringing; the id is the one the conversation shows. state: done means the work was accomplished, and it can be reopened: when the person says it is not done ("it's not working"), set state: active on that same Goal instead of starting a new one; its parent reopens with it. state: done while children are still open records your part as done: the Goal waits and closes by itself when its last open child closes. cancelled is final. Returns the Goal as get_goal reads it, at its new revision.`;
|
|
14403
|
+
var CLAIM_GOAL_DESCRIPTION2 = "Claim a pending answer to your question or the oldest runnable or review-pending Goal you own. Pass goalId to join any Goal of your person; its assignment stays unchanged. Read others before overlapping another agent\u2019s work. Joining lets you contribute and change its state; use update_goal with ownerParticipant when the assignment itself should change. Returns the Goal as get_goal reads it, and marks you as on it, which never shuts another agent out: other agents of your person may write on it and change it too, and two changes at once are told apart by revision (409 goal_revision_conflict).";
|
|
14213
14404
|
var CHECK_REPLIES_DESCRIPTION2 = "What is waiting for you: your open Deliveries (a request the user started toward you, a handoff), one row each with its Goals, how many decisions are still open, and the newest words in brief; `assigned`, your Goals nobody has started yet, however they became yours (claim_goal({goalId}) starts one); and `review`, your Goals with something new on them \u2014 the user's answers and notes are Entries on the Goal, not Deliveries. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread a Delivery in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true clears the Goal from `review` and closes the Deliveries addressed to you on it.";
|
|
14214
14405
|
var CheckRepliesSchema2 = external_exports.object({}).strict();
|
|
14215
14406
|
var AGENT_TOOLS2 = [
|
|
14407
|
+
{ name: "who_is_working", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use get_goal to read a Goal and its other contributors before starting overlapping work. Read-only; no arguments.", inputSchema: mcpInputSchema2(CheckRepliesSchema2) },
|
|
14216
14408
|
{ name: "contact", description: CONTACT_DESCRIPTION2, inputSchema: CONTACT_SCHEMA2 },
|
|
14217
14409
|
{ name: "check_replies", description: CHECK_REPLIES_DESCRIPTION2, inputSchema: mcpInputSchema2(CheckRepliesSchema2) },
|
|
14218
14410
|
{ name: "create_goal", description: CREATE_GOAL_DESCRIPTION2, inputSchema: mcpInputSchema2(CreateGoalToolSchema2) },
|
|
@@ -14221,6 +14413,156 @@ var AGENT_TOOLS2 = [
|
|
|
14221
14413
|
{ name: "update_goal", description: UPDATE_GOAL_DESCRIPTION2, inputSchema: mcpInputSchema2(UpdateGoalToolSchema2) }
|
|
14222
14414
|
];
|
|
14223
14415
|
var AGENT_TOOL_NAMES2 = AGENT_TOOLS2.map((t) => t.name);
|
|
14416
|
+
var id2 = external_exports.string().uuid();
|
|
14417
|
+
var key2 = external_exports.string().min(1).max(40);
|
|
14418
|
+
var participant2 = external_exports.string().regex(/^(human|agent):.+$/, "a participant ID such as agent:<uuid>");
|
|
14419
|
+
var SourceSchema2 = external_exports.object({
|
|
14420
|
+
entryId: id2,
|
|
14421
|
+
span: external_exports.object({ start: external_exports.number().int().min(0), end: external_exports.number().int().min(1) }).strict().refine((s) => s.end > s.start, "a span ends after it starts").optional()
|
|
14422
|
+
}).strict();
|
|
14423
|
+
var ResultRefSchema2 = external_exports.union([external_exports.object({ id: id2 }).strict(), external_exports.object({ local: key2 }).strict()]);
|
|
14424
|
+
var NodeRefSchema2 = external_exports.object({ type: external_exports.enum(["goal", "question"]), id: id2 }).strict();
|
|
14425
|
+
var DependencySchema2 = external_exports.object({
|
|
14426
|
+
blocker: NodeRefSchema2,
|
|
14427
|
+
blocked: NodeRefSchema2,
|
|
14428
|
+
action: external_exports.enum(["start", "complete", "answer"]),
|
|
14429
|
+
reason: external_exports.string().max(500).optional()
|
|
14430
|
+
}).strict();
|
|
14431
|
+
var goalState2 = external_exports.enum(["open", "completed", "canceled"]);
|
|
14432
|
+
var existingGoalChanges2 = [
|
|
14433
|
+
external_exports.object({ kind: external_exports.literal("edit"), goalId: id2, title: external_exports.string().min(1).max(120).optional(), outcome: external_exports.string().min(1).optional() }).strict().refine((c) => c.title !== void 0 || c.outcome !== void 0, "an edit changes the title or the outcome"),
|
|
14434
|
+
external_exports.object({ kind: external_exports.literal("state"), goalId: id2, state: goalState2 }).strict(),
|
|
14435
|
+
external_exports.object({ kind: external_exports.literal("assign"), goalId: id2, ownerId: participant2 }).strict(),
|
|
14436
|
+
external_exports.object({ kind: external_exports.literal("defer"), goalId: id2, until: external_exports.string().datetime().nullable() }).strict(),
|
|
14437
|
+
external_exports.object({ kind: external_exports.literal("move"), goalId: id2, parentGoalId: id2.nullable() }).strict(),
|
|
14438
|
+
external_exports.object({
|
|
14439
|
+
kind: external_exports.literal("dependency"),
|
|
14440
|
+
change: external_exports.enum(["add", "remove"]),
|
|
14441
|
+
goalId: id2,
|
|
14442
|
+
dependsOnGoalId: id2,
|
|
14443
|
+
action: external_exports.enum(["start", "complete"]),
|
|
14444
|
+
reason: external_exports.string().max(500).optional()
|
|
14445
|
+
}).strict()
|
|
14446
|
+
];
|
|
14447
|
+
var GoalChangeSchema2 = external_exports.union([
|
|
14448
|
+
external_exports.object({
|
|
14449
|
+
kind: external_exports.literal("create"),
|
|
14450
|
+
title: external_exports.string().min(1).max(120),
|
|
14451
|
+
outcome: external_exports.string().min(1),
|
|
14452
|
+
ownerId: participant2,
|
|
14453
|
+
parentGoalId: id2.optional(),
|
|
14454
|
+
sourceEntryIds: external_exports.array(id2)
|
|
14455
|
+
}).strict(),
|
|
14456
|
+
...existingGoalChanges2
|
|
14457
|
+
]);
|
|
14458
|
+
var BrainGoalChangeSchema2 = external_exports.union([
|
|
14459
|
+
// newSession: Paigy starts a session to own it (owner, 2026-10-06); until it has, the person owns it.
|
|
14460
|
+
external_exports.object({
|
|
14461
|
+
kind: external_exports.literal("create"),
|
|
14462
|
+
outcome: external_exports.string().min(1),
|
|
14463
|
+
title: external_exports.string().min(1).max(120),
|
|
14464
|
+
ownerId: participant2,
|
|
14465
|
+
parentGoal: ResultRefSchema2.nullable(),
|
|
14466
|
+
newSession: external_exports.literal(true).optional()
|
|
14467
|
+
}).strict(),
|
|
14468
|
+
...existingGoalChanges2
|
|
14469
|
+
]);
|
|
14470
|
+
var option2 = external_exports.object({ id: external_exports.string().min(1).max(40), label: external_exports.string().min(1), hint: external_exports.string().optional() }).strict();
|
|
14471
|
+
var QuestionChangeSchema2 = external_exports.union([
|
|
14472
|
+
external_exports.object({
|
|
14473
|
+
kind: external_exports.literal("create"),
|
|
14474
|
+
text: external_exports.string().min(1),
|
|
14475
|
+
answererId: participant2,
|
|
14476
|
+
goal: ResultRefSchema2.optional(),
|
|
14477
|
+
blocks: external_exports.array(external_exports.object({ question: ResultRefSchema2 }).strict()),
|
|
14478
|
+
options: external_exports.array(option2).min(2).optional(),
|
|
14479
|
+
pickMode: external_exports.enum(["one", "many", "rank"]).optional()
|
|
14480
|
+
}).strict().refine((c) => c.options === void 0 === (c.pickMode === void 0), "options and pickMode come together"),
|
|
14481
|
+
external_exports.object({ kind: external_exports.literal("edit"), questionId: id2, text: external_exports.string().min(1) }).strict(),
|
|
14482
|
+
external_exports.object({ kind: external_exports.literal("assign"), questionId: id2, answererId: participant2 }).strict(),
|
|
14483
|
+
external_exports.object({ kind: external_exports.literal("withdraw"), questionId: id2 }).strict(),
|
|
14484
|
+
external_exports.object({ kind: external_exports.literal("dependency"), change: external_exports.enum(["add", "remove"]), edge: DependencySchema2 }).strict()
|
|
14485
|
+
]);
|
|
14486
|
+
var lessonScope2 = external_exports.union([
|
|
14487
|
+
external_exports.object({ kind: external_exports.literal("user") }).strict(),
|
|
14488
|
+
external_exports.object({ kind: external_exports.literal("goal"), goal: ResultRefSchema2 }).strict()
|
|
14489
|
+
]);
|
|
14490
|
+
var LessonChangeSchema2 = external_exports.union([
|
|
14491
|
+
external_exports.object({ kind: external_exports.literal("remember"), text: external_exports.string().min(1), scope: lessonScope2 }).strict(),
|
|
14492
|
+
external_exports.object({ kind: external_exports.literal("revise"), lessonId: id2, text: external_exports.string().min(1), scope: lessonScope2 }).strict(),
|
|
14493
|
+
external_exports.object({ kind: external_exports.literal("withdraw"), lessonId: id2 }).strict()
|
|
14494
|
+
]);
|
|
14495
|
+
var BrainNextSchema2 = external_exports.object({
|
|
14496
|
+
instructions: external_exports.array(external_exports.object({ entryKeys: external_exports.array(key2).min(1) }).strict()),
|
|
14497
|
+
say: external_exports.array(key2),
|
|
14498
|
+
then: external_exports.enum(["listen", "hold", "end", "none"]),
|
|
14499
|
+
waitFor: external_exports.array(ResultRefSchema2),
|
|
14500
|
+
reason: external_exports.string()
|
|
14501
|
+
}).strict();
|
|
14502
|
+
var messageBase2 = {
|
|
14503
|
+
key: key2,
|
|
14504
|
+
text: external_exports.string().min(1),
|
|
14505
|
+
goals: external_exports.array(ResultRefSchema2),
|
|
14506
|
+
questions: external_exports.array(ResultRefSchema2),
|
|
14507
|
+
entryKeys: external_exports.array(key2),
|
|
14508
|
+
obligationIds: external_exports.array(id2)
|
|
14509
|
+
};
|
|
14510
|
+
var BrainMessageSchema2 = external_exports.union([
|
|
14511
|
+
external_exports.object({ ...messageBase2, to: external_exports.object({ kind: external_exports.literal("user") }).strict() }).strict(),
|
|
14512
|
+
external_exports.object({
|
|
14513
|
+
...messageBase2,
|
|
14514
|
+
to: external_exports.object({ kind: external_exports.literal("session"), sessionId: id2 }).strict(),
|
|
14515
|
+
call: external_exports.object({ callId: id2, listen: external_exports.literal(true) }).strict().optional()
|
|
14516
|
+
}).strict()
|
|
14517
|
+
]);
|
|
14518
|
+
var BrainSearchSchema2 = external_exports.object({
|
|
14519
|
+
query: external_exports.string().min(1).max(500),
|
|
14520
|
+
within: external_exports.array(external_exports.enum(["entries", "goals", "answers"])).min(1),
|
|
14521
|
+
goalId: id2.optional()
|
|
14522
|
+
}).strict();
|
|
14523
|
+
var BrainResultSchema2 = external_exports.object({
|
|
14524
|
+
messages: external_exports.array(BrainMessageSchema2),
|
|
14525
|
+
entries: external_exports.array(external_exports.object({ key: key2, sources: external_exports.array(SourceSchema2).min(1), goals: external_exports.array(ResultRefSchema2) }).strict()),
|
|
14526
|
+
questions: external_exports.array(external_exports.object({ key: key2, entryKeys: external_exports.array(key2), change: QuestionChangeSchema2 }).strict()),
|
|
14527
|
+
answers: external_exports.array(external_exports.object({
|
|
14528
|
+
question: ResultRefSchema2,
|
|
14529
|
+
entryKeys: external_exports.array(key2).min(1),
|
|
14530
|
+
summary: external_exports.string().min(1),
|
|
14531
|
+
selectedOptionIds: external_exports.array(external_exports.string().min(1)).optional()
|
|
14532
|
+
}).strict()),
|
|
14533
|
+
goals: external_exports.array(external_exports.object({ key: key2, entryKeys: external_exports.array(key2), change: BrainGoalChangeSchema2 }).strict()),
|
|
14534
|
+
feedback: external_exports.array(external_exports.object({ entryKeys: external_exports.array(key2).min(1) }).strict()),
|
|
14535
|
+
lessons: external_exports.array(external_exports.object({ entryKeys: external_exports.array(key2).min(1), change: LessonChangeSchema2 }).strict()),
|
|
14536
|
+
next: BrainNextSchema2,
|
|
14537
|
+
search: BrainSearchSchema2.optional()
|
|
14538
|
+
}).strict();
|
|
14539
|
+
var str2 = external_exports.string();
|
|
14540
|
+
var strs2 = external_exports.array(external_exports.string());
|
|
14541
|
+
var CompactResultSchema2 = external_exports.object({
|
|
14542
|
+
next: external_exports.object({ say: strs2, then: external_exports.enum(["listen", "hold", "end", "none"]), waitFor: strs2, reason: str2, instructions: strs2 }).strict(),
|
|
14543
|
+
messages: external_exports.array(external_exports.object({ key: str2, to: str2, text: str2, about: strs2, entries: strs2, owed: strs2, inviteToCall: external_exports.boolean() }).strict()),
|
|
14544
|
+
entries: external_exports.array(external_exports.object({ key: str2, from: str2, start: external_exports.number().int(), end: external_exports.number().int(), on: strs2 }).strict()),
|
|
14545
|
+
answers: external_exports.array(external_exports.object({ question: str2, entries: strs2, summary: str2, picked: strs2 }).strict()),
|
|
14546
|
+
changes: external_exports.array(external_exports.object({
|
|
14547
|
+
key: str2,
|
|
14548
|
+
what: external_exports.enum(["goal", "question", "lesson"]),
|
|
14549
|
+
op: external_exports.enum(["create", "edit", "assign", "open", "complete", "cancel", "defer", "move", "block", "unblock", "withdraw", "remember", "revise"]),
|
|
14550
|
+
id: str2,
|
|
14551
|
+
text: str2,
|
|
14552
|
+
title: str2,
|
|
14553
|
+
who: str2,
|
|
14554
|
+
under: str2,
|
|
14555
|
+
gate: external_exports.enum(["start", "complete", "answer", "none"]),
|
|
14556
|
+
options: external_exports.array(external_exports.object({ id: str2, label: str2 }).strict()),
|
|
14557
|
+
pick: external_exports.enum(["one", "many", "rank", "words"]),
|
|
14558
|
+
entries: strs2
|
|
14559
|
+
}).strict()),
|
|
14560
|
+
feedback: external_exports.array(strs2),
|
|
14561
|
+
search: external_exports.object({ query: str2, within: strs2, goal: str2 }).strict()
|
|
14562
|
+
}).strict();
|
|
14563
|
+
var COMPACT_RESULT_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
14564
|
+
zodToJsonSchema2(CompactResultSchema2, { $refStrategy: "none" })
|
|
14565
|
+
);
|
|
14224
14566
|
function entryWords(entry) {
|
|
14225
14567
|
const content = entry.content;
|
|
14226
14568
|
if (content && "sealed" in content) return "";
|
|
@@ -14233,11 +14575,13 @@ function entryWords(entry) {
|
|
|
14233
14575
|
const description = plain.description;
|
|
14234
14576
|
const parts = [title, ...Array.isArray(description) ? description : []].filter((v) => typeof v === "string");
|
|
14235
14577
|
if (parts.length) return parts.join("\n\n");
|
|
14578
|
+
const line = plain.line;
|
|
14579
|
+
if (typeof line === "string" && line.trim()) return line;
|
|
14236
14580
|
}
|
|
14237
14581
|
return entry.sources.map((source) => source.text).join("\n");
|
|
14238
14582
|
}
|
|
14239
14583
|
var LIVE_MS2 = 3 * 6e4;
|
|
14240
|
-
var WORKING_MS2 =
|
|
14584
|
+
var WORKING_MS2 = 60 * 6e4;
|
|
14241
14585
|
var ContextSchema2 = external_exports.object({
|
|
14242
14586
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
14243
14587
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -14455,7 +14799,7 @@ var IntentSchema2 = external_exports.object({
|
|
|
14455
14799
|
* (`parseDelay`, #1292), and a value here simply wins over that reading. */
|
|
14456
14800
|
dueInSeconds: external_exports.number().int().positive().optional(),
|
|
14457
14801
|
/** Feedback only (#812): WHICH failure the complaint names — typed by the mapper that
|
|
14458
|
-
* already read the utterance, so `
|
|
14802
|
+
* already read the utterance, so `signals.kind` stops defaulting to
|
|
14459
14803
|
* 'other' on every row. A table that records that something was wrong and nothing
|
|
14460
14804
|
* about what cannot answer "is the bot looping less this week?". */
|
|
14461
14805
|
fault: external_exports.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
|
|
@@ -14589,7 +14933,13 @@ var AgendaTurnSchema2 = external_exports.object({
|
|
|
14589
14933
|
/** Whether the walk WAITS for an answer before moving on. Absent = derived as today
|
|
14590
14934
|
* (a question blocks, context flows). blocking:false on a question = ask and move
|
|
14591
14935
|
* on, the claim stays pending; blocking:true on context = hold for a reply. */
|
|
14592
|
-
blocking: external_exports.boolean().optional()
|
|
14936
|
+
blocking: external_exports.boolean().optional(),
|
|
14937
|
+
/** SPOKEN ONLY IF THEY SAY NOTHING (owner, 2026-10-01, call 812de935: "you're gonna re-ask, but it
|
|
14938
|
+
* shouldn't be the same words … more like, hey, are you still there, or are you able to answer, or
|
|
14939
|
+
* would you need more information"). The walk holds this turn out of its queue; at the queue's end it
|
|
14940
|
+
* listens for the last word, and only if that listen is silent is this turn said and asked. If they
|
|
14941
|
+
* speak, it is dropped and their words are taken like any reply. */
|
|
14942
|
+
ifSilent: external_exports.boolean().optional()
|
|
14593
14943
|
});
|
|
14594
14944
|
var CLAIM_STALE_MS2 = 30 * 6e4;
|
|
14595
14945
|
var InboxItemSchema2 = external_exports.object({
|
|
@@ -14733,7 +15083,18 @@ var InboxItemSchema2 = external_exports.object({
|
|
|
14733
15083
|
/** THAT GOAL'S NAME (#2416) — what Activity's row is headed by, since a row there is one Goal
|
|
14734
15084
|
* and the cards it holds sit behind it. Stamped by the same read as `goalState`. */
|
|
14735
15085
|
goalTitle: external_exports.string().optional()
|
|
14736
|
-
}).optional()
|
|
15086
|
+
}).optional(),
|
|
15087
|
+
/** WHAT THIS CARD IS, IN TWELVE CHARACTERS (#3019) — the hash of every other field on it, stamped
|
|
15088
|
+
* by the one read that serves the open list (`apps/api/src/inbox/project.ts` `inboxFor`). It is
|
|
15089
|
+
* how the incremental read knows a card has not moved: the phone echoes back the revs it holds
|
|
15090
|
+
* (`POST /api/inbox/changes`) and is sent only the cards whose rev differs.
|
|
15091
|
+
*
|
|
15092
|
+
* THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK — the same mechanism as `QueueItem.rev`
|
|
15093
|
+
* (#2928) and an entry's (#3018), for the same reason: a card shows facts no `updated_at` of its
|
|
15094
|
+
* own moves (its Goal's state and name, how cold the work behind it has gone, whether a ring is
|
|
15095
|
+
* live). Optional, so a fixture, the demo and the archive lens need not spell one, and a card
|
|
15096
|
+
* with no rev is simply always re-sent. */
|
|
15097
|
+
rev: external_exports.string().optional()
|
|
14737
15098
|
});
|
|
14738
15099
|
var APNS_TOKEN_RE2 = /^[0-9a-fA-F]{64}$/;
|
|
14739
15100
|
var PushTokenSchema2 = external_exports.object({
|
|
@@ -15105,7 +15466,22 @@ var QueueItemSchema2 = external_exports.object({
|
|
|
15105
15466
|
* minute after the person speaks erases their instant from it, and the durable traces the client
|
|
15106
15467
|
* can see (`replies`, `questions[].answeredAt`) miss a spontaneous note entirely — a `request`
|
|
15107
15468
|
* Entry with no `about_id` is in neither. */
|
|
15108
|
-
lastPersonAt: external_exports.string().nullable().optional()
|
|
15469
|
+
lastPersonAt: external_exports.string().nullable().optional(),
|
|
15470
|
+
/** WHAT THIS ROW IS, IN TWELVE CHARACTERS (#2928) — the hash of every other field on it, stamped
|
|
15471
|
+
* by the one projection that builds the row (`apps/api/src/goal/queue.ts`). It is how the
|
|
15472
|
+
* incremental read knows a row has not moved: the phone echoes back the revs it holds
|
|
15473
|
+
* (`POST /api/goals/changes`) and is sent only the rows whose rev differs.
|
|
15474
|
+
*
|
|
15475
|
+
* IT IS THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK. Nothing here reasons about which
|
|
15476
|
+
* writes change which field — the comparison is over the bytes the phone is holding, so a fact
|
|
15477
|
+
* the row shows that no `updated_at` moves for (a lease lapsing, a dependency's state, a
|
|
15478
|
+
* sibling appearing in `childGoalIds`) cannot go unnoticed. Optional because a hand-built
|
|
15479
|
+
* queue (a fixture, the demo) spells none, and a row with no rev is simply always re-sent. */
|
|
15480
|
+
rev: external_exports.string().optional()
|
|
15481
|
+
});
|
|
15482
|
+
var QueueDeltaSchema2 = external_exports.object({
|
|
15483
|
+
ids: external_exports.array(external_exports.string()),
|
|
15484
|
+
items: external_exports.array(QueueItemSchema2)
|
|
15109
15485
|
});
|
|
15110
15486
|
var COLD_AFTER_MS2 = 3 * 24 * 60 * 60 * 1e3;
|
|
15111
15487
|
var NoteSourceSchema2 = external_exports.enum(["app", "call"]);
|
|
@@ -15276,6 +15652,11 @@ var DeviceTokenSchema2 = external_exports.object({
|
|
|
15276
15652
|
/** Local host recovery must preserve the launch's runtime and Paigy identity. */
|
|
15277
15653
|
harness: external_exports.enum(["claude", "codex", "agy"]).optional(),
|
|
15278
15654
|
session_id: external_exports.string().uuid().optional(),
|
|
15655
|
+
/** A conversation the host must not resume: its context is full, so every turn fails
|
|
15656
|
+
* ("Prompt is too long"). Written when a run hits it (`run.ts` `onFull`); the host skips a slot
|
|
15657
|
+
* whose resumable session is this one, so its Goals reach the dead-agent handoff instead of a
|
|
15658
|
+
* copy that types the person's words into a turn that cannot run (Calls, 2026-10-06). */
|
|
15659
|
+
full_session: external_exports.string().optional(),
|
|
15279
15660
|
uik_pub: external_exports.string().nullable().optional()
|
|
15280
15661
|
});
|
|
15281
15662
|
var SupportRequestSchema2 = external_exports.object({
|
|
@@ -15441,11 +15822,14 @@ function sameTail(a, b) {
|
|
|
15441
15822
|
return a.length === b.length && a.every((l, i) => l === b[i]);
|
|
15442
15823
|
}
|
|
15443
15824
|
|
|
15825
|
+
// src/run.ts
|
|
15826
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
15827
|
+
|
|
15444
15828
|
// src/harness/session.ts
|
|
15445
15829
|
import { spawn } from "child_process";
|
|
15446
|
-
import { existsSync as existsSync4 } from "fs";
|
|
15830
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
|
|
15447
15831
|
import { homedir as homedir9 } from "os";
|
|
15448
|
-
import { resolve as resolve2, delimiter as delimiter3 } from "path";
|
|
15832
|
+
import { resolve as resolve2, delimiter as delimiter3, join as join9 } from "path";
|
|
15449
15833
|
|
|
15450
15834
|
// src/harness/acp.ts
|
|
15451
15835
|
var none = { events: [], writes: [] };
|
|
@@ -15518,10 +15902,10 @@ var AcpDriver = class {
|
|
|
15518
15902
|
return this.flush();
|
|
15519
15903
|
}
|
|
15520
15904
|
/** Answer a PermissionEvent (ask mode). The id is the request's JSON-RPC id as a string. */
|
|
15521
|
-
respond(
|
|
15522
|
-
const request = this.pending.get(
|
|
15905
|
+
respond(id3, decision) {
|
|
15906
|
+
const request = this.pending.get(id3);
|
|
15523
15907
|
if (!request) return [];
|
|
15524
|
-
this.pending.delete(
|
|
15908
|
+
this.pending.delete(id3);
|
|
15525
15909
|
const optionId = optionFor(request.options, decision);
|
|
15526
15910
|
return [
|
|
15527
15911
|
optionId ? frame2({ id: request.id, result: { outcome: { outcome: "selected", optionId } } }) : frame2({ id: request.id, result: { outcome: { outcome: "cancelled" } } })
|
|
@@ -15675,7 +16059,7 @@ var AcpDriver = class {
|
|
|
15675
16059
|
writes: [frame2({ id: msg.id, error: { code: -32601, message: `Method not found: ${msg.method}` } })]
|
|
15676
16060
|
};
|
|
15677
16061
|
}
|
|
15678
|
-
const
|
|
16062
|
+
const id3 = msg.id;
|
|
15679
16063
|
const options = msg.params?.options ?? [];
|
|
15680
16064
|
const title = msg.params?.toolCall?.title?.trim() || "a tool call";
|
|
15681
16065
|
const tool = msg.params?.toolCall?.kind ?? "tool";
|
|
@@ -15686,21 +16070,21 @@ var AcpDriver = class {
|
|
|
15686
16070
|
// means "don't stall the agent", never "don't tell the user".
|
|
15687
16071
|
events: [{ kind: "turn", role: "agent", text: `auto-approved: ${title}`, tools: [tool] }],
|
|
15688
16072
|
writes: [
|
|
15689
|
-
optionId ? frame2({ id, result: { outcome: { outcome: "selected", optionId } } }) : frame2({ id, result: { outcome: { outcome: "cancelled" } } })
|
|
16073
|
+
optionId ? frame2({ id: id3, result: { outcome: { outcome: "selected", optionId } } }) : frame2({ id: id3, result: { outcome: { outcome: "cancelled" } } })
|
|
15690
16074
|
]
|
|
15691
16075
|
};
|
|
15692
16076
|
}
|
|
15693
|
-
this.pending.set(String(
|
|
16077
|
+
this.pending.set(String(id3), { id: id3, options });
|
|
15694
16078
|
return {
|
|
15695
|
-
events: [{ kind: "permission", id: String(
|
|
16079
|
+
events: [{ kind: "permission", id: String(id3), tool, summary: title }],
|
|
15696
16080
|
writes: []
|
|
15697
16081
|
};
|
|
15698
16082
|
}
|
|
15699
16083
|
/** The frame that opens the conversation: a resume of `resume` while there is one to try,
|
|
15700
16084
|
* else a new session. */
|
|
15701
|
-
opening(
|
|
16085
|
+
opening(id3) {
|
|
15702
16086
|
const params = { cwd: this.cwd, mcpServers: [], ...this.meta ? { _meta: this.meta } : {} };
|
|
15703
|
-
return this.resume ? { id, method: "session/resume", params: { sessionId: this.resume, ...params } } : { id, method: "session/new", params };
|
|
16087
|
+
return this.resume ? { id: id3, method: "session/resume", params: { sessionId: this.resume, ...params } } : { id: id3, method: "session/new", params };
|
|
15704
16088
|
}
|
|
15705
16089
|
/** Send the queued prompts as one turn, if the agent can take one right now. */
|
|
15706
16090
|
flush() {
|
|
@@ -15816,6 +16200,21 @@ function startSession(opts) {
|
|
|
15816
16200
|
if (!existsSync4(cwd)) {
|
|
15817
16201
|
queueMicrotask(() => opts.onEvent({ kind: "error", message: `workspace does not exist: ${cwd}` }));
|
|
15818
16202
|
}
|
|
16203
|
+
const codexConfig = opts.harness === "codex" ? JSON.parse(process.env.CODEX_CONFIG || "{}") : null;
|
|
16204
|
+
if (codexConfig) {
|
|
16205
|
+
const key3 = "mcp_servers.paigy.env_vars";
|
|
16206
|
+
const configPath = join9(process.env.CODEX_HOME || join9(homedir9(), ".codex"), "config.toml");
|
|
16207
|
+
const saved = existsSync4(configPath) ? readFileSync5(configPath, "utf8") : "";
|
|
16208
|
+
const registered = /\[mcp_servers\.paigy\][\s\S]*?(?=\n\[|$)/.exec(saved)?.[0];
|
|
16209
|
+
const configured = codexConfig["mcp_servers.paigy.command"] || codexConfig["mcp_servers.paigy.url"] || codexConfig.mcp_servers?.paigy?.command || codexConfig.mcp_servers?.paigy?.url;
|
|
16210
|
+
if (!configured && !registered) {
|
|
16211
|
+
codexConfig["mcp_servers.paigy.command"] = "npx";
|
|
16212
|
+
codexConfig["mcp_servers.paigy.args"] = ["-y", "@paigy/mcp@latest"];
|
|
16213
|
+
}
|
|
16214
|
+
const savedVars = registered?.match(/env_vars\s*=\s*\[([\s\S]*?)\]/)?.[1] ?? "";
|
|
16215
|
+
const inherited = [...savedVars.matchAll(/["']([A-Za-z_][A-Za-z0-9_]*)["']/g)].map((match) => match[1]);
|
|
16216
|
+
codexConfig[key3] = [.../* @__PURE__ */ new Set([...inherited, ...codexConfig[key3] ?? codexConfig.mcp_servers?.paigy?.env_vars ?? [], ...CODEX_ENV])];
|
|
16217
|
+
}
|
|
15819
16218
|
const child = (opts.spawnFn ?? spawn)(opts.bin ?? ADAPTER_BIN[opts.harness], [], {
|
|
15820
16219
|
cwd,
|
|
15821
16220
|
// The adapter shells out to its vendor CLI (`claude`, `codex`), and a GUI- or
|
|
@@ -15826,7 +16225,9 @@ function startSession(opts) {
|
|
|
15826
16225
|
...process.env,
|
|
15827
16226
|
PATH: [process.env.PATH, ...probeDirs()].filter(Boolean).join(delimiter3),
|
|
15828
16227
|
...opts.token ? { PAIGY_TOKEN: opts.token } : {},
|
|
15829
|
-
...opts.
|
|
16228
|
+
...opts.instance ? { PAIGY_INSTANCE_ID: opts.instance } : {},
|
|
16229
|
+
...codexConfig ? { CODEX_CONFIG: JSON.stringify(codexConfig) } : {},
|
|
16230
|
+
...opts.session ?? opts.resume ? { PAIGY_SESSION_ID: opts.session ?? opts.resume } : {},
|
|
15830
16231
|
// A PUMP DRIVES THIS PROCESS (2026-09-22, #2265): every run `runHarness` starts has the
|
|
15831
16232
|
// bridge's pump typing answers in, so nothing inside it may start a second delivery path.
|
|
15832
16233
|
// Exported on EVERY spawn — `PAIGY_SESSION_ID` above is the conversation to resume, and a
|
|
@@ -15888,8 +16289,8 @@ function startSession(opts) {
|
|
|
15888
16289
|
send(text) {
|
|
15889
16290
|
write(driver.send(text));
|
|
15890
16291
|
},
|
|
15891
|
-
respond(
|
|
15892
|
-
write(driver.respond(
|
|
16292
|
+
respond(id3, decision) {
|
|
16293
|
+
write(driver.respond(id3, decision));
|
|
15893
16294
|
},
|
|
15894
16295
|
stop() {
|
|
15895
16296
|
write(driver.close());
|
|
@@ -16085,9 +16486,13 @@ async function finish(result, state, rail, opts) {
|
|
|
16085
16486
|
const progress = result?.trim().slice(0, 1e4) || void 0;
|
|
16086
16487
|
let revision = goal.revision;
|
|
16087
16488
|
try {
|
|
16088
|
-
const held = await rail.
|
|
16489
|
+
const held = await rail.read(goal.id);
|
|
16089
16490
|
revision = held.revision ?? revision;
|
|
16090
16491
|
const owed = state.noted?.some((b) => b.goals.includes(goal.id));
|
|
16492
|
+
if (held.state === "done" || held.state === "cancelled") {
|
|
16493
|
+
if (!owed) state.goal = void 0;
|
|
16494
|
+
return;
|
|
16495
|
+
}
|
|
16091
16496
|
if (!held.openDecisionNeedIds?.length && !owed) {
|
|
16092
16497
|
await rail.update(goal.id, { revision, changes: { state: "done", ...progress ? { progress } : {} }, reason: "The agent finished its turn." });
|
|
16093
16498
|
state.goal = void 0;
|
|
@@ -16105,17 +16510,31 @@ async function finish(result, state, rail, opts) {
|
|
|
16105
16510
|
}
|
|
16106
16511
|
async function cannotWork(report, state, rail, opts) {
|
|
16107
16512
|
state.resting = true;
|
|
16513
|
+
const say = (parentId) => rail.contact({ asks: [{ ask: report, ...parentId ? { parentId } : {} }], waiting: "none", channel: "notification" });
|
|
16108
16514
|
try {
|
|
16109
16515
|
if (!state.goal) {
|
|
16110
|
-
const claimed = await rail.claim();
|
|
16111
|
-
if (
|
|
16112
|
-
|
|
16516
|
+
const claimed = await rail.claim().catch(() => void 0);
|
|
16517
|
+
if (claimed?.goalId && claimed.revision !== void 0) state.goal = { id: claimed.goalId, revision: claimed.revision };
|
|
16518
|
+
}
|
|
16519
|
+
if (!state.goal) {
|
|
16520
|
+
await say();
|
|
16521
|
+
return;
|
|
16522
|
+
}
|
|
16523
|
+
try {
|
|
16524
|
+
await say(state.goal.id);
|
|
16525
|
+
} catch (error) {
|
|
16526
|
+
if (!/goal_parent_invalid/.test(error.message)) throw error;
|
|
16527
|
+
opts?.log?.(`\u26A0 the held Goal ${state.goal.id.slice(0, 8)} refused the notice (goal_parent_invalid) \u2014 saying it unplaced`);
|
|
16528
|
+
await say();
|
|
16113
16529
|
}
|
|
16114
|
-
await rail.contact({ asks: [{ ask: report, parentId: state.goal.id }], waiting: "none", channel: "notification" });
|
|
16115
16530
|
} catch (error) {
|
|
16116
16531
|
opts?.log?.(`\u26A0 could not say the agent cannot work: ${error.message}`);
|
|
16117
16532
|
}
|
|
16118
16533
|
}
|
|
16534
|
+
function conversationFull(result) {
|
|
16535
|
+
return /prompt is too long/i.test(result ?? "");
|
|
16536
|
+
}
|
|
16537
|
+
var FULL_NOTICE = "This agent's conversation is full, so it can no longer take a turn. Its session has ended and will not be resumed; its Goals go to the dead-agent handoff.";
|
|
16119
16538
|
async function afterTurn(event, state, rail, opts) {
|
|
16120
16539
|
if (event.failed) {
|
|
16121
16540
|
const limit = limitIn(event.result, (opts?.now ?? Date.now)());
|
|
@@ -16128,6 +16547,11 @@ async function afterTurn(event, state, rail, opts) {
|
|
|
16128
16547
|
opts?.log?.(`\u23F8 paused by the usage limit until ${limit.resets}: nothing is typed in until then, and it carries on by itself`);
|
|
16129
16548
|
return limit;
|
|
16130
16549
|
}
|
|
16550
|
+
if (conversationFull(event.result)) {
|
|
16551
|
+
if (!state.full) await cannotWork(FULL_NOTICE, state, rail, opts);
|
|
16552
|
+
state.full = true;
|
|
16553
|
+
return void 0;
|
|
16554
|
+
}
|
|
16131
16555
|
await cannotWork(`The agent stopped without finishing.
|
|
16132
16556
|
|
|
16133
16557
|
${event.result?.trim() || "No result reported."}`, state, rail, opts);
|
|
@@ -16176,11 +16600,16 @@ function beat() {
|
|
|
16176
16600
|
}
|
|
16177
16601
|
var BEAT_REPORT_MS = 10 * 6e4;
|
|
16178
16602
|
var ended = (state) => state === "done" || state === "cancelled";
|
|
16179
|
-
function unheard(brief, state, since = null, after = -1) {
|
|
16603
|
+
function unheard(brief, state, since = null, after = -1, reviewSince) {
|
|
16180
16604
|
const seen = state.seen ??= /* @__PURE__ */ new Set();
|
|
16181
16605
|
const words2 = [];
|
|
16606
|
+
const pendingAt = reviewSince ? Date.parse(reviewSince) : null;
|
|
16182
16607
|
for (const [i, e] of (brief.entries ?? []).entries()) {
|
|
16183
16608
|
if (seen.has(e.entryId) || !e.authorParticipant.startsWith("human:")) continue;
|
|
16609
|
+
if (pendingAt !== null && Date.parse(e.createdAt) < pendingAt) {
|
|
16610
|
+
seen.add(e.entryId);
|
|
16611
|
+
continue;
|
|
16612
|
+
}
|
|
16184
16613
|
if (i <= after) {
|
|
16185
16614
|
seen.add(e.entryId);
|
|
16186
16615
|
continue;
|
|
@@ -16191,6 +16620,12 @@ function unheard(brief, state, since = null, after = -1) {
|
|
|
16191
16620
|
const text = entryWords(e).trim();
|
|
16192
16621
|
if (text) words2.push(text);
|
|
16193
16622
|
}
|
|
16623
|
+
if (pendingAt !== null) for (const answer of brief.answers ?? []) {
|
|
16624
|
+
const key3 = `answer:${answer.decisionNeedId}`;
|
|
16625
|
+
if (seen.has(key3) || Date.parse(answer.acceptedAt) < pendingAt) continue;
|
|
16626
|
+
seen.add(key3);
|
|
16627
|
+
words2.push(`Answer to question ${answer.decisionNeedId}: ${JSON.stringify(answer.result)}`);
|
|
16628
|
+
}
|
|
16194
16629
|
return words2;
|
|
16195
16630
|
}
|
|
16196
16631
|
function sinceTheWork(brief) {
|
|
@@ -16233,16 +16668,16 @@ async function gather(state, rail, held) {
|
|
|
16233
16668
|
const listed = /* @__PURE__ */ new Set();
|
|
16234
16669
|
const announced = state.announced ??= /* @__PURE__ */ new Set();
|
|
16235
16670
|
if (waiting) {
|
|
16236
|
-
for (const
|
|
16671
|
+
for (const id3 of announced) if (!(waiting.assigned ?? []).some((g) => g.goalId === id3)) announced.delete(id3);
|
|
16237
16672
|
}
|
|
16238
16673
|
for (const g of waiting?.assigned ?? []) if (g.goalId !== state.goal.id && !announced.has(g.goalId)) look2(g.goalId, "assigned", g.title);
|
|
16239
16674
|
for (const g of waiting?.review ?? []) {
|
|
16240
16675
|
listed.add(g.goalId);
|
|
16241
16676
|
look2(g.goalId, "waiting work", g.title);
|
|
16242
16677
|
}
|
|
16243
|
-
for (const d of waiting?.deliveries ?? []) for (const
|
|
16244
|
-
listed.add(
|
|
16245
|
-
look2(
|
|
16678
|
+
for (const d of waiting?.deliveries ?? []) for (const id3 of d.goalIds) {
|
|
16679
|
+
listed.add(id3);
|
|
16680
|
+
look2(id3, "waiting work");
|
|
16246
16681
|
}
|
|
16247
16682
|
const moved = movedBelow(held, state);
|
|
16248
16683
|
for (const { goal, since } of moved) look2(goal.goalId, "subtree", goal.title, since);
|
|
@@ -16259,9 +16694,9 @@ async function gather(state, rail, held) {
|
|
|
16259
16694
|
}
|
|
16260
16695
|
}
|
|
16261
16696
|
read.add(goalId);
|
|
16262
|
-
const fresh = found.via === "waiting work" || found.via === "assigned" ? sinceTheWork(brief) : { after: -1 };
|
|
16697
|
+
const fresh = !brief.reviewSince && (found.via === "waiting work" || found.via === "assigned") ? sinceTheWork(brief) : { after: -1 };
|
|
16263
16698
|
if (found.via === "assigned") announced.add(goalId);
|
|
16264
|
-
heard.push({ goalId, ...found, brief, words: unheard(brief, state, since ?? null, fresh.after), ...fresh.note ? { note: fresh.note } : {} });
|
|
16699
|
+
heard.push({ goalId, ...found, brief, words: unheard(brief, state, brief.reviewSince ? null : since ?? null, fresh.after, brief.reviewSince), ...fresh.note ? { note: fresh.note } : {} });
|
|
16265
16700
|
}
|
|
16266
16701
|
for (const { goal, since } of moved) if (read.has(goal.goalId)) state.below.goals.set(goal.goalId, { since, at: goal.updatedAt ?? null });
|
|
16267
16702
|
return { heard, listed };
|
|
@@ -16280,7 +16715,7 @@ var block = (h) => [
|
|
|
16280
16715
|
...h.note ? [`The last note on it, from the agent before you: ${h.note}`] : [],
|
|
16281
16716
|
...h.words
|
|
16282
16717
|
].join("\n\n");
|
|
16283
|
-
var short = (ids) => ids.map((
|
|
16718
|
+
var short = (ids) => ids.map((id3) => id3.slice(0, 8)).join(", ");
|
|
16284
16719
|
var given = (heard) => heard.filter((h) => h.via === "assigned").map((h) => h.goalId);
|
|
16285
16720
|
async function hear(session, state, rail, held, opts) {
|
|
16286
16721
|
const log2 = opts?.log;
|
|
@@ -16347,8 +16782,8 @@ async function acknowledge(goalId, brief, state, rail, log2) {
|
|
|
16347
16782
|
if (acked.has(goalId) && acked.get(goalId) === brief.revision) return;
|
|
16348
16783
|
acked.set(goalId, brief.revision);
|
|
16349
16784
|
try {
|
|
16350
|
-
|
|
16351
|
-
const done = await rail.update(goalId, { revision:
|
|
16785
|
+
if (brief.revision === void 0) throw new Error("review_revision_missing");
|
|
16786
|
+
const done = await rail.update(goalId, { revision: brief.revision, changes: { reviewed: true }, reason: "Read by the harness session; the person's words on it are typed in." });
|
|
16352
16787
|
acked.set(goalId, done.revision);
|
|
16353
16788
|
if (state.goal?.id === goalId && done.revision !== void 0) state.goal.revision = done.revision;
|
|
16354
16789
|
} catch (error) {
|
|
@@ -16385,25 +16820,26 @@ async function look(session, state, rail, opts) {
|
|
|
16385
16820
|
brief2 = await rail.claim();
|
|
16386
16821
|
if (!brief2.goalId || brief2.revision === void 0) {
|
|
16387
16822
|
state.goal = { id: admitted.goalId, revision: admitted.revision };
|
|
16388
|
-
send2(session, state, [HARNESS_INSTRUCTION, prompt].join("\n\n"));
|
|
16823
|
+
send2(session, state, [HARNESS_INSTRUCTION, named(admitted.goalId, void 0, { outcome: prompt }), prompt].join("\n\n"));
|
|
16389
16824
|
return;
|
|
16390
16825
|
}
|
|
16391
16826
|
} else {
|
|
16392
16827
|
return;
|
|
16393
16828
|
}
|
|
16394
16829
|
}
|
|
16395
|
-
unheard(brief2, state);
|
|
16396
|
-
if (
|
|
16397
|
-
|
|
16830
|
+
const pendingWords = brief2.reviewSince ? unheard(brief2, state, null, -1, brief2.reviewSince) : null;
|
|
16831
|
+
if (!pendingWords) unheard(brief2, state);
|
|
16832
|
+
if (!ended(brief2.state)) {
|
|
16398
16833
|
state.goal = { id: brief2.goalId, revision: brief2.revision };
|
|
16399
16834
|
state.below = baseline(brief2.goalId, brief2.subtree);
|
|
16400
16835
|
}
|
|
16401
16836
|
const outcome2 = brief2.outcome?.trim() ?? "";
|
|
16402
|
-
const said = (brief2.entries ?? []).map((e) => entryWords(e).trim()).filter((w) => w && w.toLowerCase() !== outcome2.toLowerCase());
|
|
16837
|
+
const said = (pendingWords ?? (brief2.entries ?? []).map((e) => entryWords(e).trim())).filter((w) => w && w.toLowerCase() !== outcome2.toLowerCase());
|
|
16403
16838
|
const left = brief2.earlier ? brief2.earlier.omitted + brief2.earlier.shortened : 0;
|
|
16404
16839
|
const earlier = left ? `(${left} earlier entries on this Goal are left out or shortened; get_goal with history: true reads them in full.)` : "";
|
|
16405
16840
|
const fullText = [outcome2, earlier, ...said].filter(Boolean).join("\n\n");
|
|
16406
|
-
send2(session, state, [HARNESS_INSTRUCTION, fullText].filter(Boolean).join("\n\n"));
|
|
16841
|
+
send2(session, state, [HARNESS_INSTRUCTION, named(brief2.goalId, void 0, brief2), fullText].filter(Boolean).join("\n\n"));
|
|
16842
|
+
if (ended(brief2.state)) await acknowledge(brief2.goalId, brief2, state, rail, opts?.log);
|
|
16407
16843
|
return;
|
|
16408
16844
|
}
|
|
16409
16845
|
const brief = await rail.read(state.goal.id);
|
|
@@ -16412,6 +16848,7 @@ async function look(session, state, rail, opts) {
|
|
|
16412
16848
|
const resolve3 = state.asks?.get(answer.requestEntryId);
|
|
16413
16849
|
if (!resolve3) continue;
|
|
16414
16850
|
state.asks.delete(answer.requestEntryId);
|
|
16851
|
+
(state.seen ??= /* @__PURE__ */ new Set()).add(`answer:${answer.decisionNeedId}`);
|
|
16415
16852
|
resolve3(answer.result);
|
|
16416
16853
|
}
|
|
16417
16854
|
if (ended(brief.state)) {
|
|
@@ -16489,8 +16926,9 @@ function runHarness(opts) {
|
|
|
16489
16926
|
const state = { resting: true };
|
|
16490
16927
|
const clock4 = beat();
|
|
16491
16928
|
let session = null;
|
|
16492
|
-
const speaksAs = opts.
|
|
16493
|
-
const
|
|
16929
|
+
const speaksAs = opts.session ?? opts.resume ?? sessionId();
|
|
16930
|
+
const instance = randomUUID6();
|
|
16931
|
+
const rail = railFor({ token: opts.token, reach: reachAs(speaksAs, instance) });
|
|
16494
16932
|
let inbox;
|
|
16495
16933
|
if (opts.harness === "claude") {
|
|
16496
16934
|
try {
|
|
@@ -16541,11 +16979,17 @@ ${event.message}`, state, rail, { log: opts.log });
|
|
|
16541
16979
|
tail = [];
|
|
16542
16980
|
const carried = state.carriedOn && !event.failed;
|
|
16543
16981
|
if (await afterTurn(event, state, rail, { log: opts.log })) LIMITS.paused(name, Date.now());
|
|
16544
|
-
else if (
|
|
16982
|
+
else if (state.full) {
|
|
16983
|
+
opts.log(`\u25A0 ${name}'s conversation is full \u2014 ending this run; it will not be resumed`);
|
|
16984
|
+
opts.onFull?.(speaksAs);
|
|
16985
|
+
stop2();
|
|
16986
|
+
return;
|
|
16987
|
+
} else if (carried) runningAgain();
|
|
16545
16988
|
clock4.nudge("rest");
|
|
16546
16989
|
}
|
|
16547
16990
|
}
|
|
16548
16991
|
session = startSession({
|
|
16992
|
+
instance,
|
|
16549
16993
|
harness: opts.harness,
|
|
16550
16994
|
cwd: opts.cwd,
|
|
16551
16995
|
mode: opts.mode,
|
|
@@ -16589,7 +17033,7 @@ ${event.message}`, state, rail, { log: opts.log });
|
|
|
16589
17033
|
// src/update.ts
|
|
16590
17034
|
import { execFile } from "child_process";
|
|
16591
17035
|
import { createRequire } from "module";
|
|
16592
|
-
import { dirname as dirname3, join as
|
|
17036
|
+
import { dirname as dirname3, join as join10 } from "path";
|
|
16593
17037
|
import { promisify } from "util";
|
|
16594
17038
|
var SERVICE_LABEL = "ai.paigy.harness";
|
|
16595
17039
|
var VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
@@ -16616,7 +17060,7 @@ async function latestVersion() {
|
|
|
16616
17060
|
}
|
|
16617
17061
|
async function install(version) {
|
|
16618
17062
|
const bin = dirname3(process.execPath);
|
|
16619
|
-
await promisify(execFile)(
|
|
17063
|
+
await promisify(execFile)(join10(bin, "npm"), ["install", "-g", `@paigy/harness@${version}`], {
|
|
16620
17064
|
env: { ...process.env, PATH: `${bin}:${process.env.PATH ?? ""}`, npm_config_update_notifier: "false" },
|
|
16621
17065
|
timeout: 5 * 6e4
|
|
16622
17066
|
});
|
|
@@ -16667,7 +17111,7 @@ function selfUpdater(deps) {
|
|
|
16667
17111
|
}
|
|
16668
17112
|
|
|
16669
17113
|
// src/host.ts
|
|
16670
|
-
var HOST_FILE =
|
|
17114
|
+
var HOST_FILE = join11(homedir10(), ".paigy", "host.json");
|
|
16671
17115
|
function wakeHarness(slot, saved, claudeTranscript) {
|
|
16672
17116
|
if (slot === "Desktop") return void 0;
|
|
16673
17117
|
if (saved !== null) return saved === "agy" || saved === "codex" || saved === "claude" ? saved : void 0;
|
|
@@ -16679,7 +17123,7 @@ function wakeHarness(slot, saved, claudeTranscript) {
|
|
|
16679
17123
|
}
|
|
16680
17124
|
function readHostState() {
|
|
16681
17125
|
try {
|
|
16682
|
-
const s = JSON.parse(
|
|
17126
|
+
const s = JSON.parse(readFileSync6(HOST_FILE, "utf8"));
|
|
16683
17127
|
if (Date.now() - s.at > 2e4) return null;
|
|
16684
17128
|
try {
|
|
16685
17129
|
process.kill(s.pid, 0);
|
|
@@ -16701,26 +17145,33 @@ var claimedRun = (sessionId2) => {
|
|
|
16701
17145
|
return { slot, key: runKey(slot) };
|
|
16702
17146
|
};
|
|
16703
17147
|
var launchRun = (spec) => ({ session: spec.sessionId });
|
|
17148
|
+
var BOUND_ELSEWHERE_MS = 10 * 6e4;
|
|
17149
|
+
var boundElsewhere = /* @__PURE__ */ new Map();
|
|
17150
|
+
var BOUND_SKIP = "is bound to the session it was paired in, which this host cannot name \u2014 run `paigy-harness handoff` as it, in its folder, to record it";
|
|
16704
17151
|
var NOTHING_WAITING = "has nothing waiting";
|
|
16705
17152
|
async function waitingWork(token, session) {
|
|
17153
|
+
const quiet = boundElsewhere.get(token);
|
|
17154
|
+
if (quiet !== void 0 && Date.now() < quiet) return { skip: BOUND_SKIP };
|
|
16706
17155
|
let found;
|
|
16707
17156
|
try {
|
|
16708
17157
|
found = await checkReplies({ token, ...session ? { reach: reachAs(session) } : {} });
|
|
16709
17158
|
} catch (error) {
|
|
16710
17159
|
if (error instanceof RevokedError) return { skip: "was revoked: its credential is gone for good", revoked: true };
|
|
16711
17160
|
if (error instanceof ApiError && error.body?.error === "identity_session_mismatch") {
|
|
16712
|
-
|
|
17161
|
+
boundElsewhere.set(token, Date.now() + BOUND_ELSEWHERE_MS);
|
|
17162
|
+
return { skip: BOUND_SKIP };
|
|
16713
17163
|
}
|
|
16714
17164
|
return { skip: `could not read what is waiting (${error.message})` };
|
|
16715
17165
|
}
|
|
17166
|
+
boundElsewhere.delete(token);
|
|
16716
17167
|
const next = found.claimable;
|
|
16717
17168
|
const about = next ? { goalId: next.goalId, ...next.since ? { since: next.since } : {} } : {};
|
|
16718
17169
|
const n = found.deliveries.length;
|
|
16719
17170
|
if (n) return { waiting: n === 1 ? "a message" : `${n} messages`, ...about };
|
|
16720
17171
|
return next ? { waiting: `Goal ${next.goalId.slice(0, 8)}${next.title ? ` "${next.title}"` : ""}`, ...about } : { skip: NOTHING_WAITING };
|
|
16721
17172
|
}
|
|
16722
|
-
function evict(runs,
|
|
16723
|
-
if (runs.get(
|
|
17173
|
+
function evict(runs, key3, run) {
|
|
17174
|
+
if (runs.get(key3)?.run === run) runs.delete(key3);
|
|
16724
17175
|
}
|
|
16725
17176
|
var PRESENCE_FRESH_MS = 3 * 6e4;
|
|
16726
17177
|
var IDLE_HOLDER_MS = 30 * 6e4;
|
|
@@ -16763,9 +17214,9 @@ function startHost(opts) {
|
|
|
16763
17214
|
const agent2 = slotIdentity(slot).tokenId;
|
|
16764
17215
|
if (!agent2) return;
|
|
16765
17216
|
const goalId = found?.goalId;
|
|
16766
|
-
const
|
|
16767
|
-
if (recorded.get(slot) ===
|
|
16768
|
-
recorded.set(slot,
|
|
17217
|
+
const key3 = `${decision}:${goalId ?? ""}`;
|
|
17218
|
+
if (recorded.get(slot) === key3) return;
|
|
17219
|
+
recorded.set(slot, key3);
|
|
16769
17220
|
void recordDecision({
|
|
16770
17221
|
agent: agent2,
|
|
16771
17222
|
decision,
|
|
@@ -16815,7 +17266,7 @@ function startHost(opts) {
|
|
|
16815
17266
|
const specs = await claimSessions(asHost).catch(() => []);
|
|
16816
17267
|
for (const spec of specs) {
|
|
16817
17268
|
const label = spec.name ?? spec.harness;
|
|
16818
|
-
const { slot, key } = claimedRun(spec.sessionId);
|
|
17269
|
+
const { slot, key: key3 } = claimedRun(spec.sessionId);
|
|
16819
17270
|
const log2 = (line) => opts.log(`[${label}] ${line}`);
|
|
16820
17271
|
try {
|
|
16821
17272
|
saveToken({
|
|
@@ -16838,14 +17289,17 @@ function startHost(opts) {
|
|
|
16838
17289
|
token: spec.token,
|
|
16839
17290
|
...launchRun(spec),
|
|
16840
17291
|
onExit: () => {
|
|
16841
|
-
evict(runs,
|
|
17292
|
+
evict(runs, key3, run);
|
|
16842
17293
|
goneAt.set(slot, Date.now());
|
|
16843
17294
|
opts.log(`\u2716 ${label} ended`);
|
|
16844
17295
|
},
|
|
17296
|
+
onFull: (session) => {
|
|
17297
|
+
updateSlot(slot, { full_session: session });
|
|
17298
|
+
},
|
|
16845
17299
|
name: label,
|
|
16846
17300
|
log: log2
|
|
16847
17301
|
});
|
|
16848
|
-
runs.set(
|
|
17302
|
+
runs.set(key3, { run, label, token: spec.token });
|
|
16849
17303
|
void heartbeat(void 0, { token: spec.token }).catch(() => {
|
|
16850
17304
|
});
|
|
16851
17305
|
void refreshIdentities();
|
|
@@ -16897,9 +17351,9 @@ function startHost(opts) {
|
|
|
16897
17351
|
let lastHolders = /* @__PURE__ */ new Map();
|
|
16898
17352
|
async function wakeSlot(slot, { presence, holders }, cause = "sweep") {
|
|
16899
17353
|
{
|
|
16900
|
-
const
|
|
17354
|
+
const key3 = runKey(slot);
|
|
16901
17355
|
if (slot === "Desktop" || isRevoked(slot)) return;
|
|
16902
|
-
const live = runs.get(
|
|
17356
|
+
const live = runs.get(key3);
|
|
16903
17357
|
if (live) {
|
|
16904
17358
|
const theirs = (localHolderPids().get(slot) ?? []).map((p) => p.pid).filter((pid) => !descendsFrom(pid, process.pid));
|
|
16905
17359
|
if (theirs.length) {
|
|
@@ -16934,6 +17388,11 @@ function startHost(opts) {
|
|
|
16934
17388
|
}
|
|
16935
17389
|
const identity = slotIdentity(slot);
|
|
16936
17390
|
const owner = !identity.harness || identity.harness === "claude" ? ownerSession(slot) : null;
|
|
17391
|
+
const resumes = owner?.sessionId ?? identity.sessionId;
|
|
17392
|
+
if (resumes && identity.fullSession === resumes) {
|
|
17393
|
+
skip(`has a full conversation (${resumes.slice(0, 8)}) \u2014 not resuming it; its Goals go to the dead-agent handoff`);
|
|
17394
|
+
return;
|
|
17395
|
+
}
|
|
16937
17396
|
const read = async () => {
|
|
16938
17397
|
const got = await waitingWork(token, identity.sessionId ?? owner?.sessionId);
|
|
16939
17398
|
if (!("skip" in got)) return got;
|
|
@@ -16974,7 +17433,7 @@ function startHost(opts) {
|
|
|
16974
17433
|
}
|
|
16975
17434
|
found ??= await read() ?? void 0;
|
|
16976
17435
|
if (!found) return;
|
|
16977
|
-
if (runs.has(
|
|
17436
|
+
if (runs.has(key3)) {
|
|
16978
17437
|
skip("was started by the wake that read it first");
|
|
16979
17438
|
return;
|
|
16980
17439
|
}
|
|
@@ -16990,14 +17449,17 @@ function startHost(opts) {
|
|
|
16990
17449
|
// A dead run must not squat the slot — evict so the next wake can respawn. Only ITS entry:
|
|
16991
17450
|
// a run that is no longer the slot's must not take the live one's place away with it.
|
|
16992
17451
|
onExit: () => {
|
|
16993
|
-
evict(runs,
|
|
17452
|
+
evict(runs, key3, run);
|
|
16994
17453
|
goneAt.set(slot, Date.now());
|
|
16995
17454
|
opts.log(`\u2716 ${label} ended`);
|
|
16996
17455
|
},
|
|
17456
|
+
onFull: (session) => {
|
|
17457
|
+
updateSlot(slot, { full_session: session });
|
|
17458
|
+
},
|
|
16997
17459
|
name: label,
|
|
16998
17460
|
log: log2
|
|
16999
17461
|
});
|
|
17000
|
-
runs.set(
|
|
17462
|
+
runs.set(key3, { run, label, token });
|
|
17001
17463
|
void heartbeat(void 0, { token }).catch(() => {
|
|
17002
17464
|
});
|
|
17003
17465
|
const woke = `\u25B6 ${owner ? "resumed" : "woke"} ${label} (${harness}) \u2014 ${found.waiting} was waiting, in ${workspace}${decided.why === "gone" ? " (its process here ended; its last heartbeat was not waited out)" : taking}`;
|
|
@@ -17050,17 +17512,17 @@ function startHost(opts) {
|
|
|
17050
17512
|
void heartbeat(void 0, { token, activity: { lines: lines2, at: (/* @__PURE__ */ new Date()).toISOString() } }).catch(() => {
|
|
17051
17513
|
});
|
|
17052
17514
|
};
|
|
17053
|
-
for (const [
|
|
17515
|
+
for (const [key3, r] of runs) {
|
|
17054
17516
|
if (!r.token) continue;
|
|
17055
17517
|
const lines2 = r.run.tail();
|
|
17056
|
-
const was = published.get(
|
|
17518
|
+
const was = published.get(key3);
|
|
17057
17519
|
if (was && sameTail(was.lines, lines2)) continue;
|
|
17058
|
-
published.set(
|
|
17520
|
+
published.set(key3, { token: r.token, lines: lines2 });
|
|
17059
17521
|
publishTail(r.token, lines2);
|
|
17060
17522
|
}
|
|
17061
|
-
for (const [
|
|
17062
|
-
if (runs.has(
|
|
17063
|
-
published.delete(
|
|
17523
|
+
for (const [key3, was] of published) {
|
|
17524
|
+
if (runs.has(key3)) continue;
|
|
17525
|
+
published.delete(key3);
|
|
17064
17526
|
if (was.lines.length > 0) publishTail(was.token, []);
|
|
17065
17527
|
}
|
|
17066
17528
|
};
|
|
@@ -17091,25 +17553,25 @@ function startHost(opts) {
|
|
|
17091
17553
|
})));
|
|
17092
17554
|
};
|
|
17093
17555
|
const api = {
|
|
17094
|
-
sessions: () => [...runs.entries()].map(([
|
|
17556
|
+
sessions: () => [...runs.entries()].map(([id3, r]) => [id3, r.label]),
|
|
17095
17557
|
// Every identity this machine holds, plus what it's doing right now. Slots are the
|
|
17096
17558
|
// roster (hatched agents, the terminal tools, claimed sessions); a run makes one of
|
|
17097
17559
|
// them live, and a run mid-turn makes it working.
|
|
17098
17560
|
roster: () => {
|
|
17099
17561
|
const live = new Map([...runs.values()].map((r) => [r.label, r.run]));
|
|
17100
17562
|
const names2 = /* @__PURE__ */ new Map();
|
|
17101
|
-
for (const
|
|
17563
|
+
for (const key3 of listSlots()) if (key3 !== "Desktop" && !isRevoked(key3)) names2.set(slotName(key3) ?? key3, key3);
|
|
17102
17564
|
for (const label of live.keys()) if (!names2.has(label)) names2.set(label, null);
|
|
17103
17565
|
return [...names2.entries()].map(([name, slot]) => {
|
|
17104
|
-
const
|
|
17566
|
+
const id3 = slot ? slotIdentity(slot) : { voice: null, tokenId: null };
|
|
17105
17567
|
return {
|
|
17106
17568
|
name,
|
|
17107
17569
|
// The slot key rides along so the window's agent page can speak AS this agent —
|
|
17108
17570
|
// its pending reads (`checkReplies`, the same pure read the sweep does) need the
|
|
17109
17571
|
// slot's own token, and the display name is not a key.
|
|
17110
17572
|
slot,
|
|
17111
|
-
tokenId:
|
|
17112
|
-
voice:
|
|
17573
|
+
tokenId: id3.tokenId,
|
|
17574
|
+
voice: id3.voice,
|
|
17113
17575
|
// RUNNING IS ABOUT THE IDENTITY, NOT ABOUT US (owner, 2026-09-14). This was
|
|
17114
17576
|
// `live.has(name)` — "this host spawned it and it has not exited" — which is a fact
|
|
17115
17577
|
// about our own process table, not about the agent. An ordinary terminal session
|
|
@@ -17117,7 +17579,7 @@ function startHost(opts) {
|
|
|
17117
17579
|
// sessions we started ourselves. The server already knows: `last_seen_at` on the
|
|
17118
17580
|
// agent's own token, the same evidence `wakeAction` stands back for. So: a run we
|
|
17119
17581
|
// hold, OR a process the server heard from inside `PRESENCE_FRESH_MS`.
|
|
17120
|
-
running: live.has(name) || !!slot && lastHolders.has(slot) || !!
|
|
17582
|
+
running: live.has(name) || !!slot && lastHolders.has(slot) || !!id3.tokenId && Date.now() - (lastPresence.get(id3.tokenId) ?? 0) < PRESENCE_FRESH_MS,
|
|
17121
17583
|
// `working` stays ours alone — it means a run WE hold is mid-turn, which is the only
|
|
17122
17584
|
// thing a tail can tell us. A process we did not spawn has no tail here to read.
|
|
17123
17585
|
working: live.get(name)?.working() ?? false
|
|
@@ -17153,7 +17615,7 @@ var win = null;
|
|
|
17153
17615
|
function log(message) {
|
|
17154
17616
|
win?.webContents.send("paigy:log", message);
|
|
17155
17617
|
}
|
|
17156
|
-
var ICON =
|
|
17618
|
+
var ICON = join12(here, "..", "assets", "icon.png");
|
|
17157
17619
|
function createWindow() {
|
|
17158
17620
|
win = new BrowserWindow({
|
|
17159
17621
|
// Sized for the working log, which is now the window's centerpiece; the layout is a
|
|
@@ -17165,9 +17627,9 @@ function createWindow() {
|
|
|
17165
17627
|
minHeight: 520,
|
|
17166
17628
|
icon: ICON,
|
|
17167
17629
|
// Windows/Linux window icon; macOS uses the dock icon below
|
|
17168
|
-
webPreferences: { preload:
|
|
17630
|
+
webPreferences: { preload: join12(here, "preload.cjs"), contextIsolation: true, nodeIntegration: false }
|
|
17169
17631
|
});
|
|
17170
|
-
void win.loadFile(
|
|
17632
|
+
void win.loadFile(join12(here, "..", "renderer", "index.html"));
|
|
17171
17633
|
}
|
|
17172
17634
|
var host = null;
|
|
17173
17635
|
app.whenReady().then(() => {
|
|
@@ -17236,7 +17698,7 @@ ipcMain.handle("paigy:agent", async (_e, name) => {
|
|
|
17236
17698
|
});
|
|
17237
17699
|
ipcMain.handle("paigy:agent-log", (_e, name) => {
|
|
17238
17700
|
try {
|
|
17239
|
-
const all =
|
|
17701
|
+
const all = readFileSync7(join12(homedir11(), ".paigy", "host.log"), "utf8").split("\n");
|
|
17240
17702
|
const mine = all.filter((l) => l.includes(`[${name}]`) || /[▶✖◉⚠]/.test(l) && l.includes(name));
|
|
17241
17703
|
return mine.slice(-500).join("\n");
|
|
17242
17704
|
} catch {
|
|
@@ -17299,11 +17761,11 @@ ipcMain.handle("paigy:triage-accept", async (_e, opts) => {
|
|
|
17299
17761
|
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
17300
17762
|
}
|
|
17301
17763
|
});
|
|
17302
|
-
ipcMain.handle("paigy:triage-dismiss", async (_e,
|
|
17764
|
+
ipcMain.handle("paigy:triage-dismiss", async (_e, id3) => {
|
|
17303
17765
|
const token = deviceToken();
|
|
17304
17766
|
if (!token) return { ok: false, error: "This Mac isn't paired yet." };
|
|
17305
17767
|
try {
|
|
17306
|
-
await dismissTriage(
|
|
17768
|
+
await dismissTriage(id3, { token });
|
|
17307
17769
|
return { ok: true };
|
|
17308
17770
|
} catch (e) {
|
|
17309
17771
|
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|