@sema-agent/core 7.24.1 → 7.24.3
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/CHANGELOG.md +62 -0
- package/dist/agents/subagent.js +4 -1
- package/dist/brain/anthropic.js +2 -2
- package/dist/brain/errors.d.ts +6 -4
- package/dist/brain/open-responses.js +2 -2
- package/dist/brain/openai.js +2 -2
- package/dist/brain/route-adjudicator.d.ts +41 -13
- package/dist/brain/route-adjudicator.js +97 -18
- package/dist/core/checkpoint-store.d.ts +127 -102
- package/dist/core/checkpoint-store.js +33 -9
- package/dist/core/mcp-failure.d.ts +73 -0
- package/dist/core/mcp-failure.js +21 -0
- package/dist/core/mcp.d.ts +18 -1
- package/dist/core/mcp.js +33 -12
- package/dist/core/memory-engine/distiller.js +3 -3
- package/dist/core/runner/prepare-boundary-parks.js +3 -3
- package/dist/core/runner/prepare-park-ask.js +2 -2
- package/dist/core/runner/prepare-wiring-manifest.d.ts +3 -2
- package/dist/core/runner/prepare-wiring-manifest.js +3 -1
- package/dist/core/runner/resume-checkpoint-screen.js +11 -10
- package/dist/core/runner/resume-policy-outcome.js +2 -2
- package/dist/core/wiring-manifest.d.ts +20 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/prompt-assembly/packs/sema-default.js +15 -0
- package/dist/prompts/default.d.ts +1 -1
- package/dist/prompts/default.js +4 -4
- package/dist/tools/fs/bash-program-position.d.ts +5 -0
- package/dist/tools/fs/bash-program-position.js +1 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +476 -1
- package/dist/tools/fs/bash-readonly-classifier.js +98 -34
- package/dist/tools/fs/fs-bash.js +2 -325
- package/dist/tools/fs/shell-read-establish.d.ts +127 -0
- package/dist/tools/fs/shell-read-establish.js +326 -0
- package/dist/tools/fs/shell-read-plan.d.ts +113 -0
- package/dist/tools/fs/shell-read-plan.js +700 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +9 -1
- package/wire-changes/core@7.24.1.json +3 -17
- package/wire-changes/core@7.24.2.json +81 -0
- package/wire-changes/core@7.24.3.json +244 -0
|
@@ -1288,110 +1288,135 @@ export type SerializedCheckpointState = {
|
|
|
1288
1288
|
[K in keyof Required<CheckpointState>]: CheckpointState[K];
|
|
1289
1289
|
};
|
|
1290
1290
|
/**
|
|
1291
|
-
* The
|
|
1292
|
-
*
|
|
1293
|
-
*
|
|
1294
|
-
*
|
|
1295
|
-
*
|
|
1296
|
-
*
|
|
1297
|
-
*
|
|
1298
|
-
*/
|
|
1299
|
-
export declare const CURRENT_CHECKPOINT_VERSION = 1;
|
|
1300
|
-
/** design/74 R3-B: the schema version a `resource_limit` suspend stamps (v2 — it adds `resourceLedger` and a
|
|
1301
|
-
* `resource_limit` `pendingAction`/`gate` an old worker can't handle). An old worker (`MAX_SUPPORTED`=1)
|
|
1302
|
-
* rejects it pre-CAS (stays `pending`, retryable on a new worker). */
|
|
1303
|
-
export declare const RESOURCE_CHECKPOINT_VERSION = 2;
|
|
1304
|
-
/**
|
|
1305
|
-
* design/80 D-1 (version-skew downgrade fix): the schema version a **binding-bearing** human/irreversible_ask
|
|
1306
|
-
* `tool_approval` suspend stamps. These checkpoints carry the decision-action binding (`boundCallId` +
|
|
1307
|
-
* `boundInputHash`) whose enforcement lives ENTIRELY in the resuming worker's resume path. A pre-D-1 worker
|
|
1308
|
-
* (released 1.100.0: `MAX_SUPPORTED`=2, and its resume code has ZERO binding logic) would otherwise resume a
|
|
1309
|
-
* D-1-minted v1 checkpoint and execute the pending tool with NO decision-action verification — the exact
|
|
1310
|
-
* "approve vendor-A $5 → execute vendor-B $5000" circumvent the binding exists to prevent (council BLOCKER #1).
|
|
1311
|
-
* Stamping these at **v3 (> the old worker's MAX of 2)** forces a pre-D-1 worker to reject them PRE-CAS
|
|
1312
|
-
* (`unsupported_version`, stays `pending`, retried on a binding-enforcing worker) instead of silently voiding
|
|
1313
|
-
* the binding. A resource_limit suspend keeps stamping v2; a pre-binding (legacy 1.100.0) checkpoint is v1.
|
|
1314
|
-
*/
|
|
1315
|
-
export declare const BINDING_CHECKPOINT_VERSION = 3;
|
|
1316
|
-
/**
|
|
1317
|
-
* design/164 (the BINDING_CHECKPOINT_VERSION precedent replayed a third time): the schema version a suspend
|
|
1318
|
-
* stamps when its ledger CARRIES a cross-slice TOKEN allocation (`resourceLedger.totalTokens` set). The
|
|
1319
|
-
* enforcement — window clamp against the remaining allocation, exhausted-resume fail-fast — lives ENTIRELY
|
|
1320
|
-
* in the resuming worker, so a pre-164 worker (MAX_SUPPORTED=4) would accept the row, ignore
|
|
1321
|
-
* `spentTokens` as a bound, and grant a fresh full window. Stamping v5 forces it to reject PRE-CAS
|
|
1322
|
-
* (`unsupported_version`, stays `pending`, retried on an enforcing worker). A ledger with NO token total
|
|
1323
|
-
* keeps the historic stamps (v2 resource / v3 binding) — deployments not using it see zero version movement.
|
|
1291
|
+
* The checkpoint FEATURE TABLE — one row per feature that ever raised the row format. A row carries the
|
|
1292
|
+
* feature's schema version, the persisted mark by which a reader tells the feature was written on a row
|
|
1293
|
+
* (`recordedOn`, the READ face), and what a mint owes when the feature is present (`writes`, the WRITE
|
|
1294
|
+
* face — the two faces are deliberately separate columns). Minting is derived from the rows
|
|
1295
|
+
* ({@link checkpointVersionFor}: the highest version among the features present), the per-feature
|
|
1296
|
+
* constants below ARE the rows' versions, and the resume screen reads the rows — so adding a feature is
|
|
1297
|
+
* one row here plus an explicit answer at each mint site (the presence record's keys are mandatory).
|
|
1324
1298
|
*
|
|
1325
|
-
*
|
|
1326
|
-
*
|
|
1327
|
-
*
|
|
1328
|
-
*
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1334
|
-
* org-admission freeze fields (`inheritedGate.admittedOrgScopes` / `ownAdmittedOrgScopes` /
|
|
1335
|
-
* `ownAdmittedOrgWriteScope` — the adjudicated-empty record included). The enforcement — the
|
|
1336
|
-
* session/delegation freeze on resume, the tenant-admission door itself — lives ENTIRELY in the
|
|
1337
|
-
* resuming worker, so a pre-admission worker (MAX_SUPPORTED=5) would accept the row, ignore the
|
|
1338
|
-
* freeze fields, and remount the raw org scopes with no admission at all. Stamping v6 forces it to
|
|
1339
|
-
* reject PRE-CAS (`unsupported_version`, stays `pending`, retried on an enforcing worker). A
|
|
1340
|
-
* checkpoint with NO org-admission state keeps the historic stamps — deployments not using org
|
|
1341
|
-
* memory see zero version movement.
|
|
1342
|
-
*/
|
|
1343
|
-
export declare const ORG_ADMISSION_CHECKPOINT_VERSION = 6;
|
|
1344
|
-
/**
|
|
1345
|
-
* F-012 (2026-08-09 — the ORG_ADMISSION_CHECKPOINT_VERSION precedent replayed a fifth time
|
|
1346
|
-
* falsification-style finding): the schema version a suspend stamps when its state carries the F-012 durable
|
|
1347
|
-
* enforcement fields — `inheritedGate.constraintChain`/`constraintDigest` (the frozen ancestor
|
|
1348
|
-
* projection chain the resume-edit re-adjudication executes) or `delegationProvenance` (the monotonic
|
|
1349
|
-
* provenance aggregate a resume re-seeds). Both enforcements live ENTIRELY in the resuming worker: a
|
|
1350
|
-
* pre-F-012 worker (MAX_SUPPORTED=6) would accept the row, ignore constraintChain (so an approver-edited
|
|
1351
|
-
* resume executes WITHOUT the frozen ancestor deny), and restart the aggregate clean (false-clean after
|
|
1352
|
-
* pre-suspend external activity). Stamping v7 forces such a worker to reject PRE-CAS
|
|
1353
|
-
* (`unsupported_version`, stays `pending`, retried on an enforcing worker). A checkpoint with NO F-012
|
|
1354
|
-
* state keeps the historic stamps — deployments not delegating see zero version movement.
|
|
1355
|
-
*/
|
|
1356
|
-
export declare const F012_CHECKPOINT_VERSION = 7;
|
|
1357
|
-
/**
|
|
1358
|
-
* #130/#120 (2026-08-10 — the F012_CHECKPOINT_VERSION precedent replayed a sixth time): the schema
|
|
1359
|
-
* version a suspend stamps when its gate carries {@link RealApprovalGateBit}. The enforcement lives
|
|
1360
|
-
* ENTIRELY in the resuming worker (the no-org-wiring refusal, the org_unavailable resume-belt
|
|
1361
|
-
* distinction): a pre-131 worker (MAX_SUPPORTED=7) would accept the row, ignore `realApproval`, and
|
|
1362
|
-
* either redeem a governed row with no org wiring at all (#120's exact hole, replayed through version
|
|
1363
|
-
* skew) or burn the approval on the unavailable belt the bit exists to soften. Stamping v8 forces it
|
|
1364
|
-
* to reject PRE-CAS (`unsupported_version`, stays `pending`, retried on an enforcing worker). A gate
|
|
1365
|
-
* with NO `realApproval` keeps the historic stamps. NOTE (erratum 2026-08-10, downstream-measured):
|
|
1366
|
-
* "zero version movement for ungoverned deployments" is TOO WIDE — the two always-mounted integrity
|
|
1367
|
-
* policies (transcript-integrity, unverifiable-delete) mint `requiresRealApproval` asks, so a park on
|
|
1368
|
-
* one of THOSE stamps v8 with `origin:"policy"` on ANY durable deployment, org-governed or not. What
|
|
1369
|
-
* stays true: rows whose gate carries no realApproval bit keep their historic stamps everywhere.
|
|
1370
|
-
*/
|
|
1371
|
-
export declare const REAL_APPROVAL_CHECKPOINT_VERSION = 8;
|
|
1372
|
-
/**
|
|
1373
|
-
* design/199 (2026-08-13 — the precedent replayed a seventh time): the schema version a suspend
|
|
1374
|
-
* stamps when its state carries the READ-FACE section ({@link CheckpointState.readFace} — the
|
|
1375
|
-
* resolved open face and/or deny-set additions). The enforcement lives ENTIRELY in the resuming
|
|
1376
|
-
* worker (the stricter-wins face fold + the deny-entry union at prepare): a pre-199 worker
|
|
1377
|
-
* (MAX_SUPPORTED=8) would accept the row, ignore the section, and resume the leg under its own
|
|
1378
|
-
* defaults — silently DROPPING the frozen deny additions (a widening), and re-deriving the face
|
|
1379
|
-
* from live config alone. Stamping v9 forces it to reject PRE-CAS (`unsupported_version`, stays
|
|
1380
|
-
* `pending`, retried on an enforcing worker). A row whose task ran the default posture (face roots,
|
|
1381
|
-
* no additions) carries NO section and keeps the historic stamps — such a resume on ANY worker
|
|
1382
|
-
* folds to roots + built-ins, which is exactly the semantics it was cut under.
|
|
1299
|
+
* Because the stamp is a maximum, the version records only the HIGHEST feature on a row; whether a lower
|
|
1300
|
+
* feature is present is said by that feature's own mark, never by the number. A resume rule keyed on the
|
|
1301
|
+
* version alone is therefore exact to the version at which its feature was the top: `realApproval` is
|
|
1302
|
+
* version-recorded at v8 and marker-recorded (`readFace.realApproval`) from v9 on. The same law binds the
|
|
1303
|
+
* NEXT feature: once a row can be stamped above v9, "v9+ ⇒ read-face section present" no longer follows
|
|
1304
|
+
* from the stamp, and the face needs a mark of its own (or the row a per-feature presence record).
|
|
1305
|
+
*
|
|
1306
|
+
* v4 is a GAP, deliberately: the retired cross-slice wall-clock allocation. This worker still reads v4
|
|
1307
|
+
* rows; a ledger carrying the retired axis is refused by FIELD (`walltime_axis_retired`) at any version.
|
|
1383
1308
|
*/
|
|
1384
|
-
export declare const
|
|
1385
|
-
/** The
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1309
|
+
export declare const CHECKPOINT_FEATURES: {
|
|
1310
|
+
/** The baseline shape: a plain human-approval suspend before any feature raised the format. No live
|
|
1311
|
+
* mint stamps it today; it is the floor of the ladder and the reading of a row written before any feature. */
|
|
1312
|
+
readonly baseline: {
|
|
1313
|
+
readonly version: 1;
|
|
1314
|
+
readonly introduced: "design/49";
|
|
1315
|
+
readonly recordedOn: (_cp: Checkpoint) => boolean;
|
|
1316
|
+
readonly writes: "the row itself";
|
|
1317
|
+
};
|
|
1318
|
+
/** A `resource_limit` suspend: `resourceLedger` plus a `resource_limit` gate/pendingAction an older worker
|
|
1319
|
+
* cannot handle (it rejects pre-CAS; the row stays `pending`, retryable on a newer worker). */
|
|
1320
|
+
readonly resource: {
|
|
1321
|
+
readonly version: 2;
|
|
1322
|
+
readonly introduced: "design/74";
|
|
1323
|
+
readonly recordedOn: (cp: Checkpoint) => boolean;
|
|
1324
|
+
readonly writes: "resourceLedger; gate/pendingAction kind resource_limit";
|
|
1325
|
+
};
|
|
1326
|
+
/** A HUMAN-DECISION park (tool approval or plan_review) of the binding era: the decision-action binding
|
|
1327
|
+
* (`boundCallId` + `boundInputHash` on a tool_approval) is enforced ENTIRELY by the resuming worker, so a
|
|
1328
|
+
* pre-binding worker must reject the row rather than execute the pending tool unverified. Like `resource`,
|
|
1329
|
+
* this row is a park-kind FLOOR: a plan_review park carries it with no binding (it was introduced above it);
|
|
1330
|
+
* whether a tool_approval row carries its hash is the resume binding rung's own conjunction. */
|
|
1331
|
+
readonly binding: {
|
|
1332
|
+
readonly version: 3;
|
|
1333
|
+
readonly introduced: "design/80";
|
|
1334
|
+
readonly recordedOn: (cp: Checkpoint) => boolean;
|
|
1335
|
+
readonly writes: "pendingAction.boundCallId + boundInputHash on a tool_approval; a plan_review carries the stamp only";
|
|
1336
|
+
};
|
|
1337
|
+
/** A ledger carrying a cross-slice TOKEN allocation: the window clamp and the exhausted-resume fail-fast
|
|
1338
|
+
* live in the resuming worker. A ledger with no token total keeps the historic stamps. */
|
|
1339
|
+
readonly token: {
|
|
1340
|
+
readonly version: 5;
|
|
1341
|
+
readonly introduced: "design/164";
|
|
1342
|
+
readonly recordedOn: (cp: Checkpoint) => boolean;
|
|
1343
|
+
readonly writes: "resourceLedger.totalTokens";
|
|
1344
|
+
};
|
|
1345
|
+
/** Org-memory admission freeze state (the adjudicated-empty record included): the session/delegation
|
|
1346
|
+
* freeze on resume and the tenant-admission door live in the resuming worker. */
|
|
1347
|
+
readonly orgAdmission: {
|
|
1348
|
+
readonly version: 6;
|
|
1349
|
+
readonly introduced: "org-admission";
|
|
1350
|
+
readonly recordedOn: (cp: Checkpoint) => boolean;
|
|
1351
|
+
readonly writes: "inheritedGate.admittedOrgScopes / ownAdmittedOrgScopes / ownAdmittedOrgWriteScope / orgAdmissionGoverned";
|
|
1352
|
+
};
|
|
1353
|
+
/** The F-012 durable enforcement fields: the frozen ancestor projection chain + its digest (one write, the
|
|
1354
|
+
* resume-edit re-adjudication executes it) and/or the delegation-provenance aggregate a resume re-seeds. */
|
|
1355
|
+
readonly f012: {
|
|
1356
|
+
readonly version: 7;
|
|
1357
|
+
readonly introduced: "F-012";
|
|
1358
|
+
readonly recordedOn: (cp: Checkpoint) => boolean;
|
|
1359
|
+
readonly writes: "inheritedGate.constraintChain + constraintDigest together; delegationProvenance";
|
|
1360
|
+
};
|
|
1361
|
+
/** A gate carrying {@link RealApprovalGateBit}: the no-org-wiring refusal and the org_unavailable resume
|
|
1362
|
+
* belt live in the resuming worker. Minted at ONE site (the approval park); the two always-mounted
|
|
1363
|
+
* integrity policies mint it on any durable deployment, org-governed or not. */
|
|
1364
|
+
readonly realApproval: {
|
|
1365
|
+
readonly version: 8;
|
|
1366
|
+
readonly introduced: "#130/#120";
|
|
1367
|
+
readonly recordedOn: (cp: Checkpoint) => boolean;
|
|
1368
|
+
readonly writes: "gate.realApproval (a well-formed bit) + pendingAction.requiresRealApproval; from v9: readFace.realApproval = true beside it";
|
|
1369
|
+
};
|
|
1370
|
+
/** The READ-face section ({@link CheckpointState.readFace}): resolved face + normalized deny-set additions,
|
|
1371
|
+
* written only for a non-default posture; the stricter-wins fold lives in the resuming worker. Carries the
|
|
1372
|
+
* redundant governance marker for `realApproval` (see the table note above). Absent ⇒ default posture. */
|
|
1373
|
+
readonly face: {
|
|
1374
|
+
readonly version: 9;
|
|
1375
|
+
readonly introduced: "design/199";
|
|
1376
|
+
readonly recordedOn: (cp: Checkpoint) => boolean;
|
|
1377
|
+
readonly writes: "state.readFace { face, denyEntries? } for a non-default posture; realApproval: true only at the mint whose gate carries the bit (a hands-less re-park never carries the seed's marker forward)";
|
|
1378
|
+
};
|
|
1379
|
+
};
|
|
1380
|
+
/** One row of {@link CHECKPOINT_FEATURES}. */
|
|
1381
|
+
export interface CheckpointFeatureRow {
|
|
1382
|
+
readonly version: number;
|
|
1383
|
+
/** Where the feature came from — a design or ticket tag, documentation only. */
|
|
1384
|
+
readonly introduced: string;
|
|
1385
|
+
/** READ face: true iff the row carries the feature's persisted mark. */
|
|
1386
|
+
readonly recordedOn: (cp: Checkpoint) => boolean;
|
|
1387
|
+
/** WRITE face: what a mint must write when the feature is present (prose; the mint sites are the code). */
|
|
1388
|
+
readonly writes: string;
|
|
1389
|
+
}
|
|
1390
|
+
/** The feature words — the row keys of {@link CHECKPOINT_FEATURES}. */
|
|
1391
|
+
export type CheckpointFeature = keyof typeof CHECKPOINT_FEATURES;
|
|
1392
|
+
/** The BASELINE {@link Checkpoint} schema version — the `baseline` row. NOT the highest a resume accepts (that
|
|
1393
|
+
* is {@link MAX_SUPPORTED_CHECKPOINT_VERSION}); resume stays forward-compatible by `<=` against MAX, never `===`. */
|
|
1394
|
+
export declare const CURRENT_CHECKPOINT_VERSION: 1;
|
|
1395
|
+
/** The `resource` row's version. */
|
|
1396
|
+
export declare const RESOURCE_CHECKPOINT_VERSION: 2;
|
|
1397
|
+
/** The `binding` row's version. */
|
|
1398
|
+
export declare const BINDING_CHECKPOINT_VERSION: 3;
|
|
1399
|
+
/** The `token` row's version. v4 (the retired wall-clock total) is deliberately not reused. */
|
|
1400
|
+
export declare const TOKEN_CHECKPOINT_VERSION: 5;
|
|
1401
|
+
/** The `orgAdmission` row's version. */
|
|
1402
|
+
export declare const ORG_ADMISSION_CHECKPOINT_VERSION: 6;
|
|
1403
|
+
/** The `f012` row's version. */
|
|
1404
|
+
export declare const F012_CHECKPOINT_VERSION: 7;
|
|
1405
|
+
/** The `realApproval` row's version. */
|
|
1406
|
+
export declare const REAL_APPROVAL_CHECKPOINT_VERSION: 8;
|
|
1407
|
+
/** The `face` row's version. */
|
|
1408
|
+
export declare const FACE_CHECKPOINT_VERSION: 9;
|
|
1409
|
+
/** The highest {@link Checkpoint.version} `runner.resume` will act on — the top row of the table; a higher one
|
|
1410
|
+
* is rejected pre-CAS with {@link CheckpointError} `unsupported_version` (the row stays `pending`, retryable on
|
|
1411
|
+
* a newer worker). This is the CEILING entrance: the storage-faithfulness probe stamps it directly on a
|
|
1412
|
+
* synthetic row that is never resumed, and does not go through feature detection. */
|
|
1413
|
+
export declare const MAX_SUPPORTED_CHECKPOINT_VERSION: 9;
|
|
1414
|
+
/** What a mint site answers for: every row but the baseline, every key MANDATORY — a new row is a compile
|
|
1415
|
+
* error at each mint site until that site says whether its rows carry the feature. */
|
|
1416
|
+
export type CheckpointFeaturePresence = Record<Exclude<CheckpointFeature, "baseline">, boolean>;
|
|
1417
|
+
/** The schema version a mint stamps: the highest version among the features PRESENT on the row, the baseline
|
|
1418
|
+
* when none is. One rule for every mint site — the precedence between features is the version numbers. */
|
|
1419
|
+
export declare function checkpointVersionFor(presence: CheckpointFeaturePresence): number;
|
|
1395
1420
|
/**
|
|
1396
1421
|
* Read a checkpoint's schema version, defaulting an absent field to **legacy `0`** (a 1.67-era checkpoint
|
|
1397
1422
|
* written before the field existed — it carries no `workspaceHandle`, so resuming it the v1 way is safe).
|
|
@@ -186,15 +186,39 @@ export function realApprovalOrgFact(bit) {
|
|
|
186
186
|
return bit.org;
|
|
187
187
|
return bit.origin === "org_rule" ? "rule" : bit.origin === "org_unavailable" ? "unavailable" : undefined;
|
|
188
188
|
}
|
|
189
|
-
export const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
189
|
+
export const CHECKPOINT_FEATURES = {
|
|
190
|
+
baseline: { version: 1, introduced: "design/49", recordedOn: (_cp) => true, writes: "the row itself" },
|
|
191
|
+
resource: { version: 2, introduced: "design/74", recordedOn: (cp) => cp.gate.kind === "resource_limit", writes: "resourceLedger; gate/pendingAction kind resource_limit" },
|
|
192
|
+
binding: { version: 3, introduced: "design/80", recordedOn: (cp) => cp.pendingAction.kind === "tool_approval" || cp.pendingAction.kind === "plan_review", writes: "pendingAction.boundCallId + boundInputHash on a tool_approval; a plan_review carries the stamp only" },
|
|
193
|
+
token: { version: 5, introduced: "design/164", recordedOn: (cp) => cp.resourceLedger?.totalTokens !== undefined, writes: "resourceLedger.totalTokens" },
|
|
194
|
+
orgAdmission: {
|
|
195
|
+
version: 6,
|
|
196
|
+
introduced: "org-admission",
|
|
197
|
+
recordedOn: (cp) => cp.state.inheritedGate?.admittedOrgScopes !== undefined || cp.state.inheritedGate?.ownAdmittedOrgScopes !== undefined || cp.state.inheritedGate?.ownAdmittedOrgWriteScope !== undefined || cp.state.inheritedGate?.orgAdmissionGoverned === true,
|
|
198
|
+
writes: "inheritedGate.admittedOrgScopes / ownAdmittedOrgScopes / ownAdmittedOrgWriteScope / orgAdmissionGoverned",
|
|
199
|
+
},
|
|
200
|
+
f012: { version: 7, introduced: "F-012", recordedOn: (cp) => cp.state.inheritedGate?.constraintChain !== undefined || cp.state.delegationProvenance !== undefined, writes: "inheritedGate.constraintChain + constraintDigest together; delegationProvenance" },
|
|
201
|
+
realApproval: { version: 8, introduced: "#130/#120", recordedOn: (cp) => cp.gate.kind === "irreversible_ask" && cp.gate.realApproval !== undefined, writes: "gate.realApproval (a well-formed bit) + pendingAction.requiresRealApproval; from v9: readFace.realApproval = true beside it" },
|
|
202
|
+
face: { version: 9, introduced: "design/199", recordedOn: (cp) => cp.state.readFace !== undefined, writes: "state.readFace { face, denyEntries? } for a non-default posture; realApproval: true only at the mint whose gate carries the bit (a hands-less re-park never carries the seed's marker forward)" },
|
|
203
|
+
};
|
|
204
|
+
export const CURRENT_CHECKPOINT_VERSION = CHECKPOINT_FEATURES.baseline.version;
|
|
205
|
+
export const RESOURCE_CHECKPOINT_VERSION = CHECKPOINT_FEATURES.resource.version;
|
|
206
|
+
export const BINDING_CHECKPOINT_VERSION = CHECKPOINT_FEATURES.binding.version;
|
|
207
|
+
export const TOKEN_CHECKPOINT_VERSION = CHECKPOINT_FEATURES.token.version;
|
|
208
|
+
export const ORG_ADMISSION_CHECKPOINT_VERSION = CHECKPOINT_FEATURES.orgAdmission.version;
|
|
209
|
+
export const F012_CHECKPOINT_VERSION = CHECKPOINT_FEATURES.f012.version;
|
|
210
|
+
export const REAL_APPROVAL_CHECKPOINT_VERSION = CHECKPOINT_FEATURES.realApproval.version;
|
|
211
|
+
export const FACE_CHECKPOINT_VERSION = CHECKPOINT_FEATURES.face.version;
|
|
212
|
+
export const MAX_SUPPORTED_CHECKPOINT_VERSION = CHECKPOINT_FEATURES.face.version;
|
|
213
|
+
export function checkpointVersionFor(presence) {
|
|
214
|
+
let version = CHECKPOINT_FEATURES.baseline.version;
|
|
215
|
+
for (const feature of Object.keys(CHECKPOINT_FEATURES)) {
|
|
216
|
+
const row = CHECKPOINT_FEATURES[feature];
|
|
217
|
+
if (presence[feature] === true && row.version > version)
|
|
218
|
+
version = row.version;
|
|
219
|
+
}
|
|
220
|
+
return version;
|
|
221
|
+
}
|
|
198
222
|
export function checkpointVersionOf(cp) {
|
|
199
223
|
return cp.version ?? 0;
|
|
200
224
|
}
|
|
@@ -46,6 +46,79 @@ export declare const DELIVERED_BY_KIND: {
|
|
|
46
46
|
};
|
|
47
47
|
/** The fence over the delivery table: `never` while every kind has a verdict. */
|
|
48
48
|
export type DeliveryTableCoversEveryMcpFailureKind = AssertAllKeysHandled<Exclude<McpFailureKind, keyof typeof DELIVERED_BY_KIND>>;
|
|
49
|
+
/**
|
|
50
|
+
* The closed set of {@link McpServerLiveness.state} — the engine's answer to ONE question, "can this
|
|
51
|
+
* client complete an exchange with this server":
|
|
52
|
+
* · `reachable` — an exchange COMPLETED (the dial handshaked and listed, a re-dial succeeded, or the
|
|
53
|
+
* server itself answered — an MCP-level rejection is an answer, so it proves reach
|
|
54
|
+
* even though it failed);
|
|
55
|
+
* · `unreachable` — an exchange was attempted and lost at the transport or the clock (refused, reset,
|
|
56
|
+
* closed, unspawnable, timed out);
|
|
57
|
+
* · `unknown` — an exchange was attempted and what came back does not answer the question (a status
|
|
58
|
+
* a gateway in front of the server may have written, a non-MCP payload, a thrown value
|
|
59
|
+
* with no structure this table reads).
|
|
60
|
+
* ABSENCE is the fourth reading and is NOT one of these words: no observation exists at all (the leg
|
|
61
|
+
* never reached this server, or the declaration could not be dialed). A consumer must not fold an absent
|
|
62
|
+
* record into either `unknown` or a health verdict — absent means indeterminate, and `unknown` means
|
|
63
|
+
* "we looked and could not tell".
|
|
64
|
+
*/
|
|
65
|
+
export declare const MCP_LIVENESS_STATES: readonly ["reachable", "unreachable", "unknown"];
|
|
66
|
+
export type McpLivenessState = (typeof MCP_LIVENESS_STATES)[number];
|
|
67
|
+
/**
|
|
68
|
+
* One liveness OBSERVATION about one server (see {@link MCP_LIVENESS_STATES} for the words). It answers
|
|
69
|
+
* what the engine last LEARNED, at `observedAt` (epoch ms) — never what is true now, and never a poll:
|
|
70
|
+
* the engine makes no probe of its own, so every record here is a by-product of work the task already
|
|
71
|
+
* did (the dial, a re-dial, a transport close).
|
|
72
|
+
*
|
|
73
|
+
* `errorCode` rides `unreachable` ONLY, and names the failure class that decided it (a `spawn_failed` is
|
|
74
|
+
* a missing binary, a `connect_refused` a closed port). It is deliberately absent on `unknown`: the whole
|
|
75
|
+
* point of that word is that the failure did NOT decide reachability, so carrying its class there would
|
|
76
|
+
* invite a consumer to key a reach action off evidence that says nothing about reach. The connect-time
|
|
77
|
+
* class is still reported in its own right beside this record (the status / manifest row's `errorCode`).
|
|
78
|
+
*/
|
|
79
|
+
export interface McpServerLiveness {
|
|
80
|
+
state: McpLivenessState;
|
|
81
|
+
observedAt: number;
|
|
82
|
+
errorCode?: McpFailureKind;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* What each failure CLASS says about reachability — the second disposition table over
|
|
86
|
+
* {@link MCP_FAILURE_KINDS}, beside {@link DELIVERED_BY_KIND}, and deliberately NOT derived from it:
|
|
87
|
+
* delivery asks "did the caller's request run", reachability asks "did an exchange complete", and the two
|
|
88
|
+
* part company on both edges — a `timeout` leaves delivery `unknown` while the exchange provably did not
|
|
89
|
+
* complete, and a `protocol` rejection is a failure the caller must handle while being the strongest
|
|
90
|
+
* proof of reach there is.
|
|
91
|
+
*
|
|
92
|
+
* `invalid_config` maps to `undefined` — the one class that is NOT an observation: a declaration that
|
|
93
|
+
* could not be dialed means no transport was ever attempted, so the honest record is no record (see the
|
|
94
|
+
* absence reading in {@link MCP_LIVENESS_STATES}), not a third-hand `unknown`.
|
|
95
|
+
*/
|
|
96
|
+
export declare const LIVENESS_BY_FAILURE_KIND: {
|
|
97
|
+
readonly connect_refused: "unreachable";
|
|
98
|
+
readonly connection_failed: "unreachable";
|
|
99
|
+
readonly connection_closed: "unreachable";
|
|
100
|
+
readonly http_status: "unknown";
|
|
101
|
+
readonly not_mcp_response: "unknown";
|
|
102
|
+
readonly spawn_failed: "unreachable";
|
|
103
|
+
readonly timeout: "unreachable";
|
|
104
|
+
readonly protocol: "reachable";
|
|
105
|
+
readonly invalid_config: undefined;
|
|
106
|
+
readonly unknown: "unknown";
|
|
107
|
+
};
|
|
108
|
+
/** The fence over the liveness table: `never` while every kind has a verdict. */
|
|
109
|
+
export type LivenessTableCoversEveryMcpFailureKind = AssertAllKeysHandled<Exclude<McpFailureKind, keyof typeof LIVENESS_BY_FAILURE_KIND>>;
|
|
110
|
+
/**
|
|
111
|
+
* What ONE exchange with a server tells the liveness record: the CLASS of the failure it ended in, or
|
|
112
|
+
* `"completed"` when it finished. Every round trip the engine makes — a dial, a tool call, a resource
|
|
113
|
+
* read, a listing, a refresh — reports exactly this, so the engine needs no second vocabulary and no
|
|
114
|
+
* per-site judgment about which failures "count".
|
|
115
|
+
*/
|
|
116
|
+
export type McpExchangeOutcome = McpFailureKind | "completed";
|
|
117
|
+
/** The liveness observation an exchange supports, or `undefined` when the outcome is not an observation
|
|
118
|
+
* at all (see {@link LIVENESS_BY_FAILURE_KIND}: a declaration that could never be dialed). THE single
|
|
119
|
+
* mint of the record — no two sites can disagree about the word, about which words may carry
|
|
120
|
+
* `errorCode`, or about what a completed exchange proves. */
|
|
121
|
+
export declare function livenessOf(outcome: McpExchangeOutcome, observedAt: number): McpServerLiveness | undefined;
|
|
49
122
|
/** Where the failure was judged: at the dial (connect + initial listing), where an errno on a stdio
|
|
50
123
|
* transport is the child process failing to spawn, or on a request over an established connection. */
|
|
51
124
|
export type McpFailureSite = {
|
package/dist/core/mcp-failure.js
CHANGED
|
@@ -25,6 +25,27 @@ export const DELIVERED_BY_KIND = {
|
|
|
25
25
|
invalid_config: "no",
|
|
26
26
|
unknown: "unknown",
|
|
27
27
|
};
|
|
28
|
+
export const MCP_LIVENESS_STATES = ["reachable", "unreachable", "unknown"];
|
|
29
|
+
export const LIVENESS_BY_FAILURE_KIND = {
|
|
30
|
+
connect_refused: "unreachable",
|
|
31
|
+
connection_failed: "unreachable",
|
|
32
|
+
connection_closed: "unreachable",
|
|
33
|
+
http_status: "unknown",
|
|
34
|
+
not_mcp_response: "unknown",
|
|
35
|
+
spawn_failed: "unreachable",
|
|
36
|
+
timeout: "unreachable",
|
|
37
|
+
protocol: "reachable",
|
|
38
|
+
invalid_config: undefined,
|
|
39
|
+
unknown: "unknown",
|
|
40
|
+
};
|
|
41
|
+
export function livenessOf(outcome, observedAt) {
|
|
42
|
+
if (outcome === "completed")
|
|
43
|
+
return { state: "reachable", observedAt };
|
|
44
|
+
const state = LIVENESS_BY_FAILURE_KIND[outcome];
|
|
45
|
+
if (state === undefined)
|
|
46
|
+
return undefined;
|
|
47
|
+
return state === "unreachable" ? { state, observedAt, errorCode: outcome } : { state, observedAt };
|
|
48
|
+
}
|
|
28
49
|
const CONNECT_KEEPS_THE_TABLE_VERDICT = new Set(["protocol", "http_status"]);
|
|
29
50
|
const NETWORK_CODES_NEVER_DELIVERED = new Set([
|
|
30
51
|
"ECONNREFUSED",
|
package/dist/core/mcp.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ProtocolId } from "./protocol-table.js";
|
|
2
|
-
import { type McpDelivered, type McpFailureKind } from "./mcp-failure.js";
|
|
2
|
+
import { type McpDelivered, type McpFailureKind, type McpServerLiveness } from "./mcp-failure.js";
|
|
3
3
|
import type { AgentTool } from "../internal/harness-types.js";
|
|
4
4
|
import type { ImageContent, TextContent } from "../internal/llm.js";
|
|
5
5
|
import { type McpImageResizer } from "./image-downsample.js";
|
|
@@ -344,6 +344,23 @@ export interface McpServerStatus {
|
|
|
344
344
|
* server name and the outcome) and this field stays absent.
|
|
345
345
|
*/
|
|
346
346
|
transportClosed?: boolean;
|
|
347
|
+
/**
|
|
348
|
+
* The engine's last liveness OBSERVATION about this server ({@link McpServerLiveness}) — a different
|
|
349
|
+
* question from all three of its neighbours, and the note each of them carries says so:
|
|
350
|
+
* · {@link status} is the CONNECT-TIME verdict of this leg, frozen at the dial — it is not this;
|
|
351
|
+
* · {@link transportClosed} is one EVENT (a close notification arrived), on the transports that have
|
|
352
|
+
* one — it is not this (this record also carries an `unreachable` that no close ever announced);
|
|
353
|
+
* · `McpReconnectResult.outcome` is one ACTION's judgment — it is not this (it says what a re-dial
|
|
354
|
+
* did, this says what the connection layer last learned).
|
|
355
|
+
* Written where the connection layer establishes or loses a connection (the dial, a re-dial, a close,
|
|
356
|
+
* a request that comes back with the transport gone), never on a timer and never by a probe: the
|
|
357
|
+
* engine adds no traffic of its own to answer it.
|
|
358
|
+
*
|
|
359
|
+
* ABSENT = no observation ({@link import("./mcp-failure.js").MCP_LIVENESS_STATES}): a declaration that could not be dialed
|
|
360
|
+
* (`invalid_config`) never touched a transport, and an older / foreign record predates the field. A
|
|
361
|
+
* consumer must read absence as indeterminate — never as healthy, never as off.
|
|
362
|
+
*/
|
|
363
|
+
liveness?: McpServerLiveness;
|
|
347
364
|
}
|
|
348
365
|
/**
|
|
349
366
|
* design/108 ① (CC 2.1.187 parity) — the GLOBAL `mcp__` marker prefix. CC names every MCP tool
|
package/dist/core/mcp.js
CHANGED
|
@@ -6,7 +6,7 @@ import { findNamespacePrefixCollision, mintNamespacePrefix, mintNamespacedToolNa
|
|
|
6
6
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7
7
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
8
8
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
9
|
-
import { classifyMcpFailure, describeHttpTransportFailure } from "./mcp-failure.js";
|
|
9
|
+
import { classifyMcpFailure, describeHttpTransportFailure, livenessOf } from "./mcp-failure.js";
|
|
10
10
|
import { lstat, mkdir, writeFile } from "node:fs/promises";
|
|
11
11
|
import { tmpdir } from "node:os";
|
|
12
12
|
import { join } from "node:path";
|
|
@@ -213,6 +213,7 @@ function rethrowHonestMcpError(err, ctx) {
|
|
|
213
213
|
};
|
|
214
214
|
sanitizeMcpErrorTextInPlace(err);
|
|
215
215
|
if (ctx.idle?.signal.reason === IDLE_WATCHDOG_ABORT_REASON) {
|
|
216
|
+
ctx.liveness?.observe?.("timeout");
|
|
216
217
|
const serverLabel = inlineUntrusted(ctx.server);
|
|
217
218
|
finish(markMcpFailure(new Error(`${ctx.what} on MCP server "${serverLabel}" received no response for ${ctx.idle.idleMs}ms (idle watchdog — ` +
|
|
218
219
|
`distinct from the ${ctx.timeoutMs}ms total ceiling above). The server may still be alive but is not ` +
|
|
@@ -224,6 +225,7 @@ function rethrowHonestMcpError(err, ctx) {
|
|
|
224
225
|
const serverLabel = inlineUntrusted(ctx.server);
|
|
225
226
|
const fenceServerText = (label, raw) => delimitUntrusted(label, truncateMcpErrorText(raw));
|
|
226
227
|
const failure = classifyMcpFailure(err, { phase: "request" });
|
|
228
|
+
ctx.liveness?.observe?.(failure.kind);
|
|
227
229
|
const details = (extra) => ({ code: failure.kind, delivered: failure.delivered, server: ctx.server, ...extra });
|
|
228
230
|
switch (failure.kind) {
|
|
229
231
|
case "timeout": {
|
|
@@ -721,10 +723,19 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
721
723
|
else
|
|
722
724
|
serverHandles[hIdx] = handle;
|
|
723
725
|
const hasInstructions = s.instructions !== undefined;
|
|
726
|
+
const observe = (outcome) => {
|
|
727
|
+
if (disposing)
|
|
728
|
+
return;
|
|
729
|
+
const observed = livenessOf(outcome, Date.now());
|
|
730
|
+
if (observed !== undefined)
|
|
731
|
+
s.status.liveness = observed;
|
|
732
|
+
};
|
|
733
|
+
s.health.observe = observe;
|
|
724
734
|
const announceClose = () => {
|
|
725
735
|
if (disposing)
|
|
726
736
|
return;
|
|
727
737
|
s.status.transportClosed = true;
|
|
738
|
+
observe("connection_closed");
|
|
728
739
|
if (hasInstructions)
|
|
729
740
|
instructionsDelta.pendingRemovals.push(spec.name);
|
|
730
741
|
};
|
|
@@ -807,7 +818,8 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
807
818
|
else {
|
|
808
819
|
warnings.push(asServerWarning(spec, r.reason));
|
|
809
820
|
const { kind, delivered, httpStatus } = classifyMcpFailure(r.reason, { phase: "connect", transport: dialKinds[i] });
|
|
810
|
-
|
|
821
|
+
const liveness = livenessOf(kind, Date.now());
|
|
822
|
+
statuses.push({ name: spec.name, status: "failed", errorCode: kind, delivered, ...(httpStatus !== undefined ? { httpStatus } : {}), error: namedMcpFailureText(r.reason), ...(liveness !== undefined ? { liveness } : {}) });
|
|
811
823
|
}
|
|
812
824
|
}
|
|
813
825
|
const resourceTools = buildResourceTools(resourceServers, isServerRevoked);
|
|
@@ -838,6 +850,7 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
838
850
|
const priorInstructions = prior?.instructions;
|
|
839
851
|
if (prior) {
|
|
840
852
|
prior.announce.fn = undefined;
|
|
853
|
+
prior.health.observe = undefined;
|
|
841
854
|
serverHandles.splice(hIdx, 1);
|
|
842
855
|
const cIdx = clients.indexOf(prior.client);
|
|
843
856
|
if (cIdx !== -1)
|
|
@@ -851,7 +864,8 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
851
864
|
}
|
|
852
865
|
catch (err) {
|
|
853
866
|
const { kind, delivered, httpStatus } = classifyMcpFailure(err, { phase: "connect", transport: dialKind });
|
|
854
|
-
const
|
|
867
|
+
const redialLiveness = livenessOf(kind, Date.now());
|
|
868
|
+
const status = { name: server, status: "failed", errorCode: kind, delivered, ...(httpStatus !== undefined ? { httpStatus } : {}), error: namedMcpFailureText(err), ...(redialLiveness !== undefined ? { liveness: redialLiveness } : {}) };
|
|
855
869
|
const sIdx = statuses.findIndex((st) => st.name === server);
|
|
856
870
|
if (sIdx === -1)
|
|
857
871
|
statuses.push(status);
|
|
@@ -953,6 +967,7 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
953
967
|
});
|
|
954
968
|
}
|
|
955
969
|
catch (err) {
|
|
970
|
+
h.health.observe?.(classifyMcpFailure(err, { phase: "request" }).kind);
|
|
956
971
|
results.push({
|
|
957
972
|
server: h.name,
|
|
958
973
|
prefix: prefixOf(h.name),
|
|
@@ -1334,7 +1349,8 @@ function buildResourceTools(resourceServers, isServerRevoked = () => false) {
|
|
|
1334
1349
|
try {
|
|
1335
1350
|
res = await rs.client
|
|
1336
1351
|
.readResource({ uri }, { signal: watchdog.combinedSignal, timeout: timeoutMs })
|
|
1337
|
-
.catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs, writeEffect: false, signal, attributeServer: true, idle: { signal: watchdog.idleSignal, idleMs } }));
|
|
1352
|
+
.catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs, writeEffect: false, signal, attributeServer: true, idle: { signal: watchdog.idleSignal, idleMs }, liveness: rs.health }));
|
|
1353
|
+
rs.health.observe?.("completed");
|
|
1338
1354
|
}
|
|
1339
1355
|
finally {
|
|
1340
1356
|
watchdog.dispose();
|
|
@@ -1393,7 +1409,7 @@ function buildResourceTools(resourceServers, isServerRevoked = () => false) {
|
|
|
1393
1409
|
try {
|
|
1394
1410
|
const ext = rs.client.getServerCapabilities()?.extensions?.[MCP_SKILLS_EXTENSION];
|
|
1395
1411
|
if (ext?.directoryRead === true) {
|
|
1396
|
-
const r = await readDirViaExtension(rs, uri, signal, timeoutMs, watchdog).catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs, writeEffect: false, signal, attributeServer: true, idle: { signal: watchdog.idleSignal, idleMs } }));
|
|
1412
|
+
const r = await readDirViaExtension(rs, uri, signal, timeoutMs, watchdog).catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs, writeEffect: false, signal, attributeServer: true, idle: { signal: watchdog.idleSignal, idleMs }, liveness: rs.health }));
|
|
1397
1413
|
const serverSaid = (detail) => `\nThe server's error text follows as external/untrusted data:\n${delimitUntrusted(`${server} error`, truncateMcpErrorText(detail))}`;
|
|
1398
1414
|
if (r.kind === "not_found") {
|
|
1399
1415
|
return {
|
|
@@ -1421,7 +1437,8 @@ function buildResourceTools(resourceServers, isServerRevoked = () => false) {
|
|
|
1421
1437
|
const walk = await walkMcpListPages(async (cursor, remainingMs) => {
|
|
1422
1438
|
const page = await rs.client
|
|
1423
1439
|
.listResources(cursor === undefined ? undefined : { cursor }, { signal: watchdog.combinedSignal, timeout: remainingMs })
|
|
1424
|
-
.catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs: remainingMs, writeEffect: false, signal, attributeServer: true, idle: { signal: watchdog.idleSignal, idleMs } }));
|
|
1440
|
+
.catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs: remainingMs, writeEffect: false, signal, attributeServer: true, idle: { signal: watchdog.idleSignal, idleMs }, liveness: rs.health }));
|
|
1441
|
+
rs.health.observe?.("completed");
|
|
1425
1442
|
return { items: page.resources, ...(page.nextCursor !== undefined ? { nextCursor: page.nextCursor } : {}) };
|
|
1426
1443
|
}, { budgetMs: timeoutMs, onPage: () => watchdog.rearm(), fingerprintOf: listEntryFingerprint, ...(signal !== undefined ? { signal } : {}) });
|
|
1427
1444
|
const dirPrefix = uri.endsWith("/") ? uri : `${uri}/`;
|
|
@@ -1552,13 +1569,15 @@ async function connectServer(spec, principal, onElicit, imageResizer, reminderDi
|
|
|
1552
1569
|
const rawInstructions = client.getInstructions();
|
|
1553
1570
|
const instructions = rawInstructions && rawInstructions.trim() ? sanitizeUntrustedText(rawInstructions.trim()) : undefined;
|
|
1554
1571
|
const info = client.getServerVersion();
|
|
1572
|
+
const status = {
|
|
1573
|
+
name: spec.name,
|
|
1574
|
+
status: "connected",
|
|
1575
|
+
...(info ? { serverInfo: { name: info.name, version: info.version } } : {}),
|
|
1576
|
+
toolNames: serverTools.map((t) => t.name),
|
|
1577
|
+
liveness: livenessOf("completed", Date.now()),
|
|
1578
|
+
};
|
|
1555
1579
|
return {
|
|
1556
|
-
status
|
|
1557
|
-
name: spec.name,
|
|
1558
|
-
status: "connected",
|
|
1559
|
-
...(info ? { serverInfo: { name: info.name, version: info.version } } : {}),
|
|
1560
|
-
toolNames: serverTools.map((t) => t.name),
|
|
1561
|
-
},
|
|
1580
|
+
status,
|
|
1562
1581
|
client,
|
|
1563
1582
|
health,
|
|
1564
1583
|
announce,
|
|
@@ -1693,10 +1712,12 @@ function intakeListedTools(listed, spec, client, health, imageResizer, reminderD
|
|
|
1693
1712
|
writeEffect,
|
|
1694
1713
|
signal,
|
|
1695
1714
|
idle: { signal: watchdog.idleSignal, idleMs },
|
|
1715
|
+
liveness: health,
|
|
1696
1716
|
...(reminderDisclosure !== undefined
|
|
1697
1717
|
? { reminder: { mark: reminderDisclosure.mark, outlet: "mcp", ...(reminderDisclosure.counts !== undefined ? { counts: reminderDisclosure.counts } : {}) } }
|
|
1698
1718
|
: {}),
|
|
1699
1719
|
}));
|
|
1720
|
+
health.observe?.("completed");
|
|
1700
1721
|
}
|
|
1701
1722
|
finally {
|
|
1702
1723
|
watchdog.dispose();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readMember, thrownText } from "../thrown-value.js";
|
|
2
|
-
import { assertServableRequestRoot } from "../../brain/route-adjudicator.js";
|
|
2
|
+
import { assertServableRequestRoot, joinRequestUrl } from "../../brain/route-adjudicator.js";
|
|
3
3
|
export const DISTILLER_DEFAULT_MAX_INPUTS_PER_PRODUCT = 32;
|
|
4
4
|
const SIZE_CLAUSE = {
|
|
5
5
|
"dl-2": "- the index should be as small as it can be made without losing either of the above.",
|
|
@@ -146,7 +146,7 @@ export function openAiCompatChatSeat(seat) {
|
|
|
146
146
|
throw distillerConfigRefusal("memory consolidation driver chat seat needs a non-empty baseUrl.");
|
|
147
147
|
let requestRoot;
|
|
148
148
|
try {
|
|
149
|
-
requestRoot =
|
|
149
|
+
requestRoot = assertServableRequestRoot(seat.baseUrl, "memory consolidation driver chat seat baseUrl") ?? "";
|
|
150
150
|
}
|
|
151
151
|
catch (e) {
|
|
152
152
|
throw distillerConfigRefusal(thrownText(e), e);
|
|
@@ -157,7 +157,7 @@ export function openAiCompatChatSeat(seat) {
|
|
|
157
157
|
throw distillerConfigRefusal(`memory consolidation driver chat seat needs an EXPLICIT model id, got ${JSON.stringify(seat.model)} — an alias can be re-pointed upstream and would move the driver with no trace in the plan.`);
|
|
158
158
|
}
|
|
159
159
|
return async (req) => {
|
|
160
|
-
const res = await fetch(
|
|
160
|
+
const res = await fetch(joinRequestUrl(requestRoot, "/chat/completions"), {
|
|
161
161
|
method: "POST",
|
|
162
162
|
headers: { "content-type": "application/json", authorization: `Bearer ${seat.apiKey}` },
|
|
163
163
|
body: JSON.stringify({
|