@sema-agent/server 7.110.0 → 7.111.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +8 -0
- package/dist/approval-ask-audit-store.d.ts +38 -25
- package/dist/approval-ask-audit-store.js +26 -59
- package/dist/bench/s1/run-firm.d.ts +11 -0
- package/dist/bench/s1/run-firm.js +47 -32
- package/dist/boot/coordinators.js +1 -1
- package/dist/boot/session-faces.d.ts +4 -0
- package/dist/boot/session-faces.js +3 -1
- package/dist/boot/stage-07-capability-layer.d.ts +1 -0
- package/dist/boot/stage-07-capability-layer.js +4 -3
- package/dist/boot/stage-08-reapers.d.ts +1 -0
- package/dist/boot/stage-09-leader.d.ts +1 -0
- package/dist/boot/stage-09-leader.js +2 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -0
- package/dist/boot/workflow-orchestration.js +1 -1
- package/dist/http/resume-legs.js +6 -3
- package/dist/http/routes/runs.js +14 -1
- package/dist/http/routes/tasks.js +8 -6
- package/dist/http/server.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/instruction-selection.d.ts +34 -0
- package/dist/instruction-selection.js +23 -0
- package/dist/observability/corrupt-read-seat.d.ts +18 -0
- package/dist/observability/corrupt-read-seat.js +18 -0
- package/dist/observability/fail-open.d.ts +15 -3
- package/dist/observability/fail-open.js +15 -3
- package/dist/orchestration/workflow-agent-session-index.d.ts +12 -23
- package/dist/orchestration/workflow-agent-session-index.js +11 -50
- package/dist/orchestration/workflow-notify-journal.d.ts +11 -8
- package/dist/orchestration/workflow-notify-journal.js +20 -36
- package/dist/plugins/file-outcome-sink.d.ts +4 -1
- package/dist/plugins/file-outcome-sink.js +9 -3
- package/dist/plugins/file-run-store.js +3 -3
- package/dist/plugins/retention-store-sql.d.ts +1 -1
- package/dist/plugins/retention-store-sql.js +2 -0
- package/dist/plugins/store-backend.js +1 -0
- package/dist/plugins/store-contracts.d.ts +19 -2
- package/dist/plugins/store-contracts.js +9 -0
- package/dist/plugins/user-input-ledger-file.d.ts +4 -2
- package/dist/plugins/user-input-ledger-file.js +11 -1
- package/dist/plugins/user-input-ledger-sql.d.ts +14 -4
- package/dist/plugins/user-input-ledger-sql.js +46 -18
- package/dist/project-memory.d.ts +10 -2
- package/dist/project-memory.js +5 -2
- package/dist/runs.js +5 -3
- package/dist/trace/ledger-sink.d.ts +4 -0
- package/dist/trace/ledger-sink.js +1 -1
- package/dist/trace/project.d.ts +11 -3
- package/dist/trace/project.js +6 -5
- package/dist/trace/self-minted-wire.d.ts +26 -0
- package/dist/trace/self-minted-wire.js +9 -0
- package/dist/trace/wire-projection-faces.d.ts +3 -3
- package/dist/trace/wire-projection-faces.js +4 -2
- package/dist/user-input-ledger.d.ts +101 -48
- package/dist/user-input-ledger.js +52 -35
- package/package.json +2 -2
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import { mkdirSync, appendFileSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
2
|
+
import { AppendLog, ensureDir } from "@sema-agent/core";
|
|
3
3
|
export class FileOutcomeSink {
|
|
4
4
|
path;
|
|
5
|
+
log;
|
|
5
6
|
constructor(root) {
|
|
6
7
|
const dir = join(root, "outcomes");
|
|
7
|
-
|
|
8
|
+
ensureDir(dir);
|
|
8
9
|
this.path = join(dir, "outcomes.jsonl");
|
|
9
10
|
}
|
|
10
11
|
async recordCore(o) {
|
|
11
|
-
|
|
12
|
+
this.log ??= new AppendLog(this.path);
|
|
13
|
+
this.log.append({ at: Date.now(), ...o }, false);
|
|
14
|
+
}
|
|
15
|
+
dispose() {
|
|
16
|
+
this.log?.close();
|
|
17
|
+
this.log = undefined;
|
|
12
18
|
}
|
|
13
19
|
}
|
|
14
20
|
//# sourceMappingURL=file-outcome-sink.js.map
|
|
@@ -7,7 +7,7 @@ import { randomBytes } from "node:crypto";
|
|
|
7
7
|
import { AppendLog, atomicWriteFile, ensureDir, isProcessLive, processFingerprint, sanitizePathComponent, writeThenLink } from "@sema-agent/core";
|
|
8
8
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
9
9
|
import { replayJsonlSkipping } from "../observability/corrupt-read-seat.js";
|
|
10
|
-
import { RUN_STALLED_ERROR, StoreBootRefusalError } from "./store-contracts.js";
|
|
10
|
+
import { RUN_ORPHANED_ERROR, RUN_STALLED_ERROR, StoreBootRefusalError, buildRunOrphanedError } from "./store-contracts.js";
|
|
11
11
|
import { notifyRunTerminal } from "../observability/run-terminal.js";
|
|
12
12
|
import { migrateRetiredAskOrigin } from "./one-time-migrations.js";
|
|
13
13
|
const ownerEq = (a, b) => (a ?? null) === (b ?? null);
|
|
@@ -603,7 +603,7 @@ export class FileRunStore {
|
|
|
603
603
|
for (const r of orphans) {
|
|
604
604
|
r.status = "failed";
|
|
605
605
|
r.result = null;
|
|
606
|
-
r.error =
|
|
606
|
+
r.error = RUN_ORPHANED_ERROR;
|
|
607
607
|
r.updatedAt = new Date();
|
|
608
608
|
this.persist(r);
|
|
609
609
|
}
|
|
@@ -808,7 +808,7 @@ export class FileRunStore {
|
|
|
808
808
|
pidStillRunning: owner !== undefined && isProcessLive({ pid: owner.pid }),
|
|
809
809
|
note: "the claim's owner process is provably not the one that wrote it (fingerprint falsified, or the pid is gone) and the row has nothing left to resume — taking the claim over",
|
|
810
810
|
});
|
|
811
|
-
return { kind: "stale", reason:
|
|
811
|
+
return { kind: "stale", reason: buildRunOrphanedError(who), snapshot };
|
|
812
812
|
}
|
|
813
813
|
takeOverClaim(sessionId, reason, snapshot) {
|
|
814
814
|
const nowClaim = readClaimRecord(singleRecordOf(this.readRecords(this.activePath(sessionId), CLAIM_RECORD_SHAPE)), sessionId);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* core 把破坏性级联整个下沉成 store 方法(`expireCheckpoints` / `deleteExpiredSessions` /
|
|
9
9
|
* `deleteOrphanToolResults`),并点名 server 的 session-purge 顺序契约(E21,`boot/session-faces.ts` 的
|
|
10
10
|
* `purgeSession` 协调器)为参考实现。那条顺序契约横跨**十余张表**(run 账本 / checkpoint / tool_result /
|
|
11
|
-
* anchor / exemption / policy / attachment / snapshot / workflow inbox / session_meta+event),而设计稿
|
|
11
|
+
* anchor / exemption / policy / attachment / 用户输入受理账本 / snapshot / workflow inbox / session_meta+event),而设计稿
|
|
12
12
|
* §6 的 F5 又要求「审计行与删除同一个 DB 事务」—— 一个事务 ⇒ 一条连接 ⇒ **一个属主**。
|
|
13
13
|
*
|
|
14
14
|
* ⇒ 布局裁定:本能力**不能**挂在 session / checkpoint / tool-result 任何一只店上(每只店只拥有自己的表,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
2
2
|
import { TASK_LIST_ITEM_TABLE, TASK_LIST_META_TABLE, taskListKeyFor } from "./task-list-store-sql.js";
|
|
3
|
+
import { USER_INPUT_ACCEPTANCE_TABLE } from "./user-input-ledger-sql.js";
|
|
3
4
|
import { ensureIndexes } from "./ensure-index.js";
|
|
4
5
|
export const MANAGED_RETENTION = "managed";
|
|
5
6
|
export const UNMANAGED_RETENTION = "none";
|
|
@@ -382,6 +383,7 @@ export class SqlRetentionStore {
|
|
|
382
383
|
["session_policy", "session_id"],
|
|
383
384
|
["session_policy_rev_mark", "session_id"],
|
|
384
385
|
...(this.opts.taskAttachmentTable ? [["task_attachment", "session_id"]] : []),
|
|
386
|
+
[USER_INPUT_ACCEPTANCE_TABLE, "session_id"],
|
|
385
387
|
["file_history_tracked", "scope"],
|
|
386
388
|
["file_history_version", "scope"],
|
|
387
389
|
["file_history_boundary", "scope"],
|
|
@@ -277,6 +277,7 @@ class LocalBackend {
|
|
|
277
277
|
this.approvalAskStore.dispose();
|
|
278
278
|
this.sendFileLedgerStore.dispose();
|
|
279
279
|
this.userInputLedgerStore.dispose();
|
|
280
|
+
this.outcomeSinkInst.dispose();
|
|
280
281
|
this.workflowJournalStore.dispose();
|
|
281
282
|
this.checkpointStoreInst?.close();
|
|
282
283
|
this.permissionRuleStoresInst?.dispose();
|
|
@@ -106,11 +106,28 @@ export type PersistedTaskResult = TaskResult & {
|
|
|
106
106
|
};
|
|
107
107
|
/**
|
|
108
108
|
* 🔴 run 店「持有它的实例丢了」那一判的**行上落款**(S-928 codex r1-F1 提为常量):三只 run 店的 `reapStale` 把心跳过期的 running 行翻成
|
|
109
|
-
* `failed` 并写下这一句。它是 run
|
|
110
|
-
* (
|
|
109
|
+
* `failed` 并写下这一句。它是 run 店死亡判决留在行上的证据之一(另一只 = {@link RUN_ORPHANED_ERROR})—— 用户输入受理账本的收敛判词
|
|
110
|
+
* (S-969 L8 起两车道同一只)据 {@link isHolderDeathVerdict} 在 reap **之后**仍能判出「写者死了」。改这一句 = 改那条判据;所以三个写者与那一个读者都只引用本常量。读时折叠成同一句话的 wire 展示站点
|
|
111
111
|
* (`routes/runs.ts` 的 stale 折叠、`routes/trace-usage.ts` 的 WORKER_DOWN 帧)刻意留字面量:它们由文案冻结门逐字钉着,且不参与任何判据。
|
|
112
112
|
*/
|
|
113
113
|
export declare const RUN_STALLED_ERROR = "run stalled (instance lost?)";
|
|
114
|
+
/**
|
|
115
|
+
* 🔴 File 车道「持有它的进程随重启没了」那一判的行上落款(S-969 L8 提为常量):boot 孤儿回收(`FileRunStore.reclaimOrphanedAtBoot`,
|
|
116
|
+
* 数据根 LOCK 作证前世进程已死 ⇒ 仍 running 的行无条件判孤儿)与运行期 claim 接管的 running 臂(`judgeClaim`,外来 + owner 已死 + 无可续)
|
|
117
|
+
* 都写这一句;接管臂在括号里附上前任持有者的进程指纹(取证用,形 = {@link buildRunOrphanedError})。与 {@link RUN_STALLED_ERROR} 同一种身份:
|
|
118
|
+
* run 店写在行上的**死亡判决**,读者 = {@link isHolderDeathVerdict}。
|
|
119
|
+
*/
|
|
120
|
+
export declare const RUN_ORPHANED_ERROR = "run orphaned by engine restart";
|
|
121
|
+
/** File 车道两个孤儿落款写口共用的那一句(`heldBy` = 接管臂的前任持有者描述;boot 回收臂不带)。 */
|
|
122
|
+
export declare function buildRunOrphanedError(heldBy?: string): string;
|
|
123
|
+
/**
|
|
124
|
+
* run 行上是不是一份**持有者死亡判决**:`failed`,且 `error` 是 run 店的两只死亡落款之一 —— {@link RUN_STALLED_ERROR}(三只 run 店的
|
|
125
|
+
* `reapStale`:心跳过期 ⇒ 实例丢了)或 {@link RUN_ORPHANED_ERROR}(File 车道:前世进程随重启没了;接管臂带括注)。判决只说「持有它的那个进程
|
|
126
|
+
* 死在这条腿收场之前」,不说是谁 —— 持有者是谁读行上的 `instanceId`(两只落款都不改它)。唯一读者 = 用户输入受理账本的收敛判词
|
|
127
|
+
* (`src/user-input-ledger.ts` `judgeOpen`);改任一句落款 = 改那条判据,所以写者与读者都只引用本模块的常量。
|
|
128
|
+
* 正常失败(别的 error)/ park 过期收割(`approval expired before decision`:腿 park 过)都**不是**死亡判决。
|
|
129
|
+
*/
|
|
130
|
+
export declare function isHolderDeathVerdict(run: Pick<RunRecord, "status" | "error">): boolean;
|
|
114
131
|
export interface RunRecord {
|
|
115
132
|
taskId: string;
|
|
116
133
|
sessionId: string;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
export class StoreBootRefusalError extends Error {
|
|
2
2
|
}
|
|
3
3
|
export const RUN_STALLED_ERROR = "run stalled (instance lost?)";
|
|
4
|
+
export const RUN_ORPHANED_ERROR = "run orphaned by engine restart";
|
|
5
|
+
export function buildRunOrphanedError(heldBy) {
|
|
6
|
+
return heldBy === undefined ? RUN_ORPHANED_ERROR : `${RUN_ORPHANED_ERROR} (claim held by ${heldBy}, which is gone)`;
|
|
7
|
+
}
|
|
8
|
+
export function isHolderDeathVerdict(run) {
|
|
9
|
+
if (run.status !== "failed" || run.error === null)
|
|
10
|
+
return false;
|
|
11
|
+
return run.error === RUN_STALLED_ERROR || run.error === RUN_ORPHANED_ERROR || run.error.startsWith(`${RUN_ORPHANED_ERROR} (`);
|
|
12
|
+
}
|
|
4
13
|
export const RUN_STATUS_CLASS = {
|
|
5
14
|
running: "live",
|
|
6
15
|
completed: "succeeded",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Logger } from "../observability/logger.js";
|
|
2
|
-
import { UserInputLedgerFold, type NewUserInputAcceptance, type OpenScanCursor, type SettleHow, type UserInputAcceptance, type UserInputLedgerRecord, type UserInputLedgerStore } from "../user-input-ledger.js";
|
|
2
|
+
import { UserInputLedgerFold, type NewUserInputAcceptance, type OpenScanCursor, type OpenScanPage, type SettleHow, type UserInputAcceptance, type UserInputLedgerRecord, type UserInputLedgerStore } from "../user-input-ledger.js";
|
|
3
3
|
/** 账本文件名(取证按文件字节对账 —— 崩溃语义的真源是文件)。 */
|
|
4
4
|
export declare const USER_INPUT_LEDGER_FILE = "ledger.jsonl";
|
|
5
5
|
/** 纯内存孪生(测试 / 夹具)。 */
|
|
@@ -13,9 +13,11 @@ export declare class MemoryUserInputLedgerStore implements UserInputLedgerStore
|
|
|
13
13
|
settle(acceptId: string, how: SettleHow, atMs: number): Promise<boolean>;
|
|
14
14
|
converge(acceptId: string, atMs: number): Promise<boolean>;
|
|
15
15
|
announce(acceptId: string, taskId: string, atMs: number): Promise<boolean>;
|
|
16
|
-
listOpen(after?: OpenScanCursor, limit?: number): Promise<
|
|
16
|
+
listOpen(after?: OpenScanCursor, limit?: number): Promise<OpenScanPage>;
|
|
17
17
|
listCrashed(sessionId: string): Promise<UserInputAcceptance[]>;
|
|
18
18
|
listUnannounced(sessionId: string, limit: number): Promise<UserInputAcceptance[]>;
|
|
19
|
+
/** S-965:会话树删除 = 一行会话级墓碑(守卫:该会话此刻有行 ⇒ 落;否则零写、回 0)。写抛 ⇒ 原样抛(协调器据此不删会话本体)。 */
|
|
20
|
+
deleteBySession(sessionId: string): Promise<number>;
|
|
19
21
|
}
|
|
20
22
|
/** local 车道(File)孪生。 */
|
|
21
23
|
export declare class FileUserInputLedgerStore extends MemoryUserInputLedgerStore {
|
|
@@ -39,6 +39,10 @@ export class MemoryUserInputLedgerStore {
|
|
|
39
39
|
async listUnannounced(sessionId, limit) {
|
|
40
40
|
return this.fold.listUnannounced(sessionId, limit);
|
|
41
41
|
}
|
|
42
|
+
async deleteBySession(sessionId) {
|
|
43
|
+
const n = this.fold.sessionSize(sessionId);
|
|
44
|
+
return (await this.write({ kind: "erased", sessionId, at: Date.now() })) ? n : 0;
|
|
45
|
+
}
|
|
42
46
|
}
|
|
43
47
|
const RECORD_SHAPE = {
|
|
44
48
|
what: "user-input ledger record of an unknown kind or with a missing / out-of-vocabulary member",
|
|
@@ -46,8 +50,14 @@ const RECORD_SHAPE = {
|
|
|
46
50
|
const o = record;
|
|
47
51
|
const str = (k) => (typeof o[k] === "string" && o[k].length > 0 ? o[k] : undefined);
|
|
48
52
|
const at = typeof o.at === "number" && Number.isFinite(o.at) ? o.at : undefined;
|
|
53
|
+
if (at === undefined)
|
|
54
|
+
return undefined;
|
|
55
|
+
if (o.kind === "erased") {
|
|
56
|
+
const sessionId = str("sessionId");
|
|
57
|
+
return sessionId === undefined ? undefined : { kind: "erased", sessionId, at };
|
|
58
|
+
}
|
|
49
59
|
const acceptId = str("acceptId");
|
|
50
|
-
if (acceptId === undefined
|
|
60
|
+
if (acceptId === undefined)
|
|
51
61
|
return undefined;
|
|
52
62
|
switch (o.kind) {
|
|
53
63
|
case "accepted": {
|
|
@@ -13,13 +13,22 @@
|
|
|
13
13
|
* 无 owner / principal / scope 列(行随它挂靠的会话走 —— 收编形Ⅲ);`writer` 是受理进程的 `instanceId`(随进程消亡,不是租户轴)。
|
|
14
14
|
* 行里不存输入正文(只有身份与时刻)。
|
|
15
15
|
*
|
|
16
|
-
* ##
|
|
17
|
-
*
|
|
16
|
+
* ## 会话树删除(S-965)
|
|
17
|
+
* `deleteBySession` = `DELETE … WHERE session_id = ?`(真删行;File 孪生是一行会话级墓碑,同义)。两条删会话树的级联各有一臂:在线删除协调器
|
|
18
|
+
* 经本动词;托管留存的事务化孪生(`retention-store-sql.ts` `purgeSessionTrees` ④)在**自己的事务**里对本表发同一只谓词(`session_id` 单键,
|
|
19
|
+
* 多值集合按方言)—— 它不调店,理由见该文件顶注的布局裁定(一个事务 ⇒ 一条连接 ⇒ 一个属主)。谓词走 `(session_id, state, accepted_at_ms)` 索引的前缀。
|
|
20
|
+
*
|
|
21
|
+
* ## 读不出的行(S-969 L6):逐行跳过,游标按原始行推进
|
|
22
|
+
* 三只读口都按 `state` 列取行(`= 'open'` / `= 'converged'`),所以 `state` 词表外的行任何读口都取不到;被取到的行上读不出的列 —— `input_kind`
|
|
23
|
+
* 词表外(新版本写的输入族:滚动发布 / 回滚的混版窗)、收敛行缺 `state_at_ms` —— 由 {@link readRows} 逐行跳过 + 计 F 类 tag
|
|
24
|
+
* `server.user-input-ledger.corrupt-record-skipped`(File 孪生重放坏行的同一只登记座),其余行照交。修前一行读不出 ⇒ 整页抛:收敛断点不前进
|
|
25
|
+
* (其后的 open 行永不收敛)、同会话的通告与读面一并失效。`listOpen` 的续翻游标取**原始**末行的键(`accept_id` / `accepted_at_ms` 是 NOT NULL
|
|
26
|
+
* 键列,坏行也读得出),跳过的行照样推进;页里少了几行不等于扫到底(那由原始行数判)。SQL 文本 / DDL 不变。
|
|
18
27
|
*/
|
|
19
28
|
import type { Pool as MysqlPool } from "mysql2/promise";
|
|
20
29
|
import type { Pool as PgPool } from "pg";
|
|
21
30
|
import { type SqlDriver } from "./sql-driver.js";
|
|
22
|
-
import { type NewUserInputAcceptance, type OpenScanCursor, type SettleHow, type UserInputAcceptance, type UserInputLedgerStore } from "../user-input-ledger.js";
|
|
31
|
+
import { type NewUserInputAcceptance, type OpenScanCursor, type OpenScanPage, type SettleHow, type UserInputAcceptance, type UserInputLedgerStore } from "../user-input-ledger.js";
|
|
23
32
|
export declare const USER_INPUT_ACCEPTANCE_TABLE = "user_input_acceptance";
|
|
24
33
|
export declare class SqlUserInputLedgerStore implements UserInputLedgerStore {
|
|
25
34
|
private readonly db;
|
|
@@ -29,9 +38,10 @@ export declare class SqlUserInputLedgerStore implements UserInputLedgerStore {
|
|
|
29
38
|
settle(acceptId: string, how: SettleHow, atMs: number): Promise<boolean>;
|
|
30
39
|
converge(acceptId: string, atMs: number): Promise<boolean>;
|
|
31
40
|
announce(acceptId: string, taskId: string, atMs: number): Promise<boolean>;
|
|
32
|
-
listOpen(after?: OpenScanCursor, limit?: number): Promise<
|
|
41
|
+
listOpen(after?: OpenScanCursor, limit?: number): Promise<OpenScanPage>;
|
|
33
42
|
listCrashed(sessionId: string): Promise<UserInputAcceptance[]>;
|
|
34
43
|
listUnannounced(sessionId: string, limit: number): Promise<UserInputAcceptance[]>;
|
|
44
|
+
deleteBySession(sessionId: string): Promise<number>;
|
|
35
45
|
}
|
|
36
46
|
/** MySQL 协议腿的构造绑定。 */
|
|
37
47
|
export declare class TiDBUserInputLedgerStore extends SqlUserInputLedgerStore {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { recordFailOpen } from "../observability/fail-open.js";
|
|
1
2
|
import { isDupKeyError } from "./sql-errors.js";
|
|
2
3
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
3
4
|
import { assertPageLimit, CRASHED_CAUSE, SETTLE_HOW_WORDS, USER_INPUT_KIND_WORDS, USER_INPUT_OPEN_SCAN_MAX, USER_INPUT_SESSION_READ_MAX, } from "../user-input-ledger.js";
|
|
@@ -12,35 +13,57 @@ function rowState(r) {
|
|
|
12
13
|
case "settled": {
|
|
13
14
|
const how = String(r["settle_how"]);
|
|
14
15
|
if (!Object.hasOwn(SETTLE_HOW_WORDS, how) || !Number.isFinite(at))
|
|
15
|
-
|
|
16
|
+
return undefined;
|
|
16
17
|
return { kind: "settled", how: how, atMs: at };
|
|
17
18
|
}
|
|
18
19
|
case "converged":
|
|
19
20
|
if (!Number.isFinite(at))
|
|
20
|
-
|
|
21
|
+
return undefined;
|
|
21
22
|
return { kind: "converged", cause: CRASHED_CAUSE, atMs: at };
|
|
22
23
|
default:
|
|
23
|
-
|
|
24
|
+
return undefined;
|
|
24
25
|
}
|
|
25
|
-
throw new Error(`${USER_INPUT_ACCEPTANCE_TABLE} row ${String(r["accept_id"])}: unreadable state (${String(r["state"])} / ${String(r["settle_how"])} / ${String(r["state_at_ms"])})`);
|
|
26
26
|
}
|
|
27
|
-
function
|
|
27
|
+
function readRow(r) {
|
|
28
28
|
const input = String(r["input_kind"]);
|
|
29
29
|
if (!Object.hasOwn(USER_INPUT_KIND_WORDS, input))
|
|
30
|
-
|
|
30
|
+
return { ok: false, reason: `input_kind ${input} is not a ledger word` };
|
|
31
|
+
const state = rowState(r);
|
|
32
|
+
if (state === undefined)
|
|
33
|
+
return { ok: false, reason: `unreadable state (${String(r["state"])} / ${String(r["settle_how"])} / ${String(r["state_at_ms"])})` };
|
|
31
34
|
const announcedAt = r["announced_at_ms"] == null ? undefined : Number(r["announced_at_ms"]);
|
|
32
35
|
return {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
ok: true,
|
|
37
|
+
acceptance: {
|
|
38
|
+
acceptId: String(r["accept_id"]),
|
|
39
|
+
input: input,
|
|
40
|
+
messageId: String(r["message_id"]),
|
|
41
|
+
sessionId: String(r["session_id"]),
|
|
42
|
+
taskId: String(r["task_id"]),
|
|
43
|
+
writer: String(r["writer"]),
|
|
44
|
+
acceptedAtMs: Number(r["accepted_at_ms"]),
|
|
45
|
+
state,
|
|
46
|
+
...(announcedAt !== undefined ? { announced: { atMs: announcedAt, taskId: String(r["announced_task_id"]) } } : {}),
|
|
47
|
+
},
|
|
42
48
|
};
|
|
43
49
|
}
|
|
50
|
+
function readRows(rows, op) {
|
|
51
|
+
const out = [];
|
|
52
|
+
for (const r of rows) {
|
|
53
|
+
const read = readRow(r);
|
|
54
|
+
if (read.ok)
|
|
55
|
+
out.push(read.acceptance);
|
|
56
|
+
else
|
|
57
|
+
recordFailOpen("server.user-input-ledger.corrupt-record-skipped", `table=${USER_INPUT_ACCEPTANCE_TABLE} op=${op} accept_id=${String(r["accept_id"])} ${read.reason}`);
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
function rawCursor(r) {
|
|
62
|
+
const acceptedAtMs = Number(r["accepted_at_ms"]);
|
|
63
|
+
if (!Number.isFinite(acceptedAtMs))
|
|
64
|
+
throw new Error(`${USER_INPUT_ACCEPTANCE_TABLE} row ${String(r["accept_id"])}: accepted_at_ms ${String(r["accepted_at_ms"])} is not a number — cannot page past it`);
|
|
65
|
+
return { acceptedAtMs, acceptId: String(r["accept_id"]) };
|
|
66
|
+
}
|
|
44
67
|
export class SqlUserInputLedgerStore {
|
|
45
68
|
db;
|
|
46
69
|
constructor(db) {
|
|
@@ -76,16 +99,21 @@ export class SqlUserInputLedgerStore {
|
|
|
76
99
|
const { rows } = after === undefined
|
|
77
100
|
? await this.db.query(this.q(`SELECT ${COLUMNS} FROM ${USER_INPUT_ACCEPTANCE_TABLE} WHERE state = 'open' ORDER BY accepted_at_ms, accept_id LIMIT ${limit}`, `SELECT ${COLUMNS} FROM ${USER_INPUT_ACCEPTANCE_TABLE} WHERE state = 'open' ORDER BY accepted_at_ms, accept_id LIMIT ${limit}`), [])
|
|
78
101
|
: await this.db.query(this.q(`SELECT ${COLUMNS} FROM ${USER_INPUT_ACCEPTANCE_TABLE} WHERE state = 'open' AND (accepted_at_ms > ? OR (accepted_at_ms = ? AND accept_id > ?)) ORDER BY accepted_at_ms, accept_id LIMIT ${limit}`, `SELECT ${COLUMNS} FROM ${USER_INPUT_ACCEPTANCE_TABLE} WHERE state = 'open' AND (accepted_at_ms > $1 OR (accepted_at_ms = $2 AND accept_id > $3)) ORDER BY accepted_at_ms, accept_id LIMIT ${limit}`), [after.acceptedAtMs, after.acceptedAtMs, after.acceptId]);
|
|
79
|
-
|
|
102
|
+
const open = readRows(rows, "listOpen");
|
|
103
|
+
return rows.length === limit ? { open, next: rawCursor(rows[rows.length - 1]) } : { open };
|
|
80
104
|
}
|
|
81
105
|
async listCrashed(sessionId) {
|
|
82
106
|
const { rows } = await this.db.query(this.q(`SELECT ${COLUMNS} FROM ${USER_INPUT_ACCEPTANCE_TABLE} WHERE session_id = ? AND state = 'converged' ORDER BY accepted_at_ms DESC, accept_id DESC LIMIT ${USER_INPUT_SESSION_READ_MAX}`, `SELECT ${COLUMNS} FROM ${USER_INPUT_ACCEPTANCE_TABLE} WHERE session_id = $1 AND state = 'converged' ORDER BY accepted_at_ms DESC, accept_id DESC LIMIT ${USER_INPUT_SESSION_READ_MAX}`), [sessionId]);
|
|
83
|
-
return rows
|
|
107
|
+
return readRows(rows, "listCrashed").reverse();
|
|
84
108
|
}
|
|
85
109
|
async listUnannounced(sessionId, limit) {
|
|
86
110
|
const n = assertPageLimit("listUnannounced", limit);
|
|
87
111
|
const { rows } = await this.db.query(this.q(`SELECT ${COLUMNS} FROM ${USER_INPUT_ACCEPTANCE_TABLE} WHERE session_id = ? AND state = 'converged' AND announced_at_ms IS NULL ORDER BY accepted_at_ms, accept_id LIMIT ${n}`, `SELECT ${COLUMNS} FROM ${USER_INPUT_ACCEPTANCE_TABLE} WHERE session_id = $1 AND state = 'converged' AND announced_at_ms IS NULL ORDER BY accepted_at_ms, accept_id LIMIT ${n}`), [sessionId]);
|
|
88
|
-
return rows
|
|
112
|
+
return readRows(rows, "listUnannounced");
|
|
113
|
+
}
|
|
114
|
+
async deleteBySession(sessionId) {
|
|
115
|
+
const res = await this.db.query(this.q(`DELETE FROM ${USER_INPUT_ACCEPTANCE_TABLE} WHERE session_id = ?`, `DELETE FROM ${USER_INPUT_ACCEPTANCE_TABLE} WHERE session_id = $1`), [sessionId]);
|
|
116
|
+
return res.affected;
|
|
89
117
|
}
|
|
90
118
|
}
|
|
91
119
|
export class TiDBUserInputLedgerStore extends SqlUserInputLedgerStore {
|
package/dist/project-memory.d.ts
CHANGED
|
@@ -25,7 +25,8 @@ interface ProjectMemoryCtx {
|
|
|
25
25
|
* every CLAUDE file it finds, per directory level up the ancestor chain, whereas this loader reads one root and
|
|
26
26
|
* takes the single first hit. */
|
|
27
27
|
declare const INSTRUCTION_FILE_CHAIN: readonly ["CLAUDE.md", ".claude/CLAUDE.md", "AGENTS.md", ".claude/AGENTS.md"];
|
|
28
|
-
|
|
28
|
+
/** 一个链成员(相对项目根的拼写)。导出给 S-961 的 wire 段与它的值闭集登记(`trace/self-minted-wire.ts`)—— 链长一员,两处 tsc 跟着红。 */
|
|
29
|
+
export type InstructionChainMember = (typeof INSTRUCTION_FILE_CHAIN)[number];
|
|
29
30
|
/** Recover (root, rel) for a path the LOADER declared — every declared path is `root + one chain member`.
|
|
30
31
|
* The probe needs this to re-run the loader's parent-directory symlink gate each round (self-review: the probe
|
|
31
32
|
* checked only the final component, so swapping `.claude` for a symlink out of the repo MID-RUN made the probe
|
|
@@ -33,7 +34,7 @@ type ChainMember = (typeof INSTRUCTION_FILE_CHAIN)[number];
|
|
|
33
34
|
* calls it with the host `node:path`. */
|
|
34
35
|
export declare function instructionChainCoordinates(absPath: string, p?: PlatformPath): {
|
|
35
36
|
root: string;
|
|
36
|
-
rel:
|
|
37
|
+
rel: InstructionChainMember;
|
|
37
38
|
} | null;
|
|
38
39
|
/**
|
|
39
40
|
* Build a `loadProjectMemory` for the host lane. Returns null (skip) unless hands are enabled AND cwd is a local
|
|
@@ -54,6 +55,13 @@ export declare function instructionChainCoordinates(absPath: string, p?: Platfor
|
|
|
54
55
|
* an absolute local directory) is "not applicable", not "none" — it never reaches the mint (O6, see `LoadOutcome`). The member is the one the snapshot carries — a cache hit answers the cached pick, so
|
|
55
56
|
* the notice follows what the model actually reads, not the disk (a same-process edit changes nothing until the
|
|
56
57
|
* snapshot is re-read: restart, post-compact, or a new session).
|
|
58
|
+
*
|
|
59
|
+
* S-961 — the same statement (same predicate, same member) also settles the calling LEG's `wiring_manifest.instructions`
|
|
60
|
+
* segment through {@link settleLegInstructionStatement} (`instruction-selection.ts`): the leg scope is the run identity
|
|
61
|
+
* the ctx does not carry, and the first call inside a leg's scope whose `ctx.sessionId` equals the leg's session is that
|
|
62
|
+
* leg's own prepare (a session-less adhoc leg falls back to plain first-call; [ref] X1). The segment projects
|
|
63
|
+
* `instructionFile`, not `instructionSources`: an empty selected file with no git narrative returns no answer and so no
|
|
64
|
+
* `instructionSources`, yet it IS the pick (it shadows every member below it) — and the notice reads the same value.
|
|
57
65
|
*/
|
|
58
66
|
export declare function makeLoadProjectMemory(opts: {
|
|
59
67
|
logger?: {
|
package/dist/project-memory.js
CHANGED
|
@@ -3,6 +3,7 @@ import { execFile } from "node:child_process";
|
|
|
3
3
|
import { createHmac, randomBytes } from "node:crypto";
|
|
4
4
|
import hostPath, { isAbsolute, join } from "node:path";
|
|
5
5
|
import { announceInstructionSource } from "./instruction-source-notice.js";
|
|
6
|
+
import { settleLegInstructionStatement } from "./instruction-selection.js";
|
|
6
7
|
import { gitSubprocessEnv } from "./server-secret-env.js";
|
|
7
8
|
import { redactSecrets } from "./trace/redact.js";
|
|
8
9
|
const INSTRUCTION_FILE_CHAIN = ["CLAUDE.md", ".claude/CLAUDE.md", "AGENTS.md", ".claude/AGENTS.md"];
|
|
@@ -92,8 +93,10 @@ export function makeLoadProjectMemory(opts) {
|
|
|
92
93
|
const seat = opts.instructionSource;
|
|
93
94
|
return async (ctx) => {
|
|
94
95
|
const outcome = await loadOutcome(ctx);
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
const statement = outcome.walked && !ctx.isSubagent ? { stated: true, file: outcome.instructionFile } : { stated: false };
|
|
97
|
+
settleLegInstructionStatement(statement, ctx.sessionId);
|
|
98
|
+
if (seat && ctx.sessionId && statement.stated)
|
|
99
|
+
await announceInstructionSource(seat, ctx.sessionId, statement.file);
|
|
97
100
|
return outcome.answer;
|
|
98
101
|
};
|
|
99
102
|
async function loadOutcome(ctx) {
|
package/dist/runs.js
CHANGED
|
@@ -6,6 +6,7 @@ import { isSessionErrorCode, sessionErrorHttp } from "./session-error-wire.js";
|
|
|
6
6
|
import { redactErrorMessage } from "./observability/run-terminal.js";
|
|
7
7
|
import { withPrincipal } from "./observability/principal-context.js";
|
|
8
8
|
import { withModelReachScope } from "./model-reach-scope.js";
|
|
9
|
+
import { createInstructionSelectionLeg } from "./instruction-selection.js";
|
|
9
10
|
import { redactSecrets } from "./trace/redact.js";
|
|
10
11
|
import { taskNotificationEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "./trace/project.js";
|
|
11
12
|
import { createLedgerSink } from "./trace/ledger-sink.js";
|
|
@@ -247,7 +248,8 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
247
248
|
metrics?.addGauge("runs_active", -1);
|
|
248
249
|
metrics?.inc("tasks_total", { status: "needs_review" });
|
|
249
250
|
};
|
|
250
|
-
const
|
|
251
|
+
const instructionLeg = createInstructionSelectionLeg();
|
|
252
|
+
const sink = createLedgerSink({ appendEvent: (seq, type, data) => runStore.appendEvent(taskId, seq, type, data), persistThinking, notifiedKeys, onActivity: (kind, statusData) => recordTurnActivity(taskId, kind, statusData), instructionsSegment: instructionLeg.segment });
|
|
251
253
|
const append = (type, data) => sink.append(type, data);
|
|
252
254
|
const flush = () => sink.flush();
|
|
253
255
|
const flushModelUsage = () => appendModelUsageDelta(append, modelUsage, taskId);
|
|
@@ -422,7 +424,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
422
424
|
await append("suggestions", { suggestions: suggestions.map((s) => redactSecrets(s)) });
|
|
423
425
|
}
|
|
424
426
|
};
|
|
425
|
-
await withModelReachScope(spec.sessionId, () => withPrincipal(principal, () => {
|
|
427
|
+
await instructionLeg.run(spec.sessionId, () => withModelReachScope(spec.sessionId, () => withPrincipal(principal, () => {
|
|
426
428
|
const withElicit = elicitation
|
|
427
429
|
? () => elicitation.runWithContext({ taskId, owner: elicitOwner ?? owner, emit: elicitEmit, abortSignal: cancelCtrl.signal }, runBody)
|
|
428
430
|
: runBody;
|
|
@@ -445,7 +447,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
445
447
|
return sendUserFile
|
|
446
448
|
? sendUserFile.runWithContext({ taskId, emit: (frame) => { const { type, ...rest } = frame; return append(type, rest); } }, withApproval)
|
|
447
449
|
: withApproval();
|
|
448
|
-
}));
|
|
450
|
+
})));
|
|
449
451
|
if (!reached && cancelCtrl.signal.aborted) {
|
|
450
452
|
try {
|
|
451
453
|
await flush();
|
|
@@ -2,6 +2,7 @@ import { type TerminalCause } from "@sema-agent/core";
|
|
|
2
2
|
import { brainStatusEventData } from "./project.js";
|
|
3
3
|
import { NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
|
|
4
4
|
import { type LedgerEventType, type ParkLedgerEventType } from "./ledger-events.js";
|
|
5
|
+
import type { InstructionsSegment } from "../instruction-selection.js";
|
|
5
6
|
/**
|
|
6
7
|
* LedgerSink — the SINGLE "stream event → durable runs-ledger" projection, shared by the background leg
|
|
7
8
|
* (runs.ts `runInBackground`) and the sync `/v1/tasks/stream` detach lane (server.ts, `x-detach-on-disconnect`).
|
|
@@ -115,5 +116,8 @@ export declare function createLedgerSink(opts: {
|
|
|
115
116
|
* 各传自己看得见的那个词(位①=流事件 `ev.type`,位②=账本行 `type`);拿不到类型的打点方
|
|
116
117
|
* (core 的 onActivity 第五席、resume 腿手动打点)省参,`kind` 诚实缺席(见 turn-activity.ts)。 */
|
|
117
118
|
onActivity?: (kind?: string, statusData?: Record<string, unknown>) => void;
|
|
119
|
+
/** S-961:这条腿的 `wiring_manifest.instructions` 段(腿的 `InstructionSelectionLeg.segment`)。**必填**:账本行与 live 帧是
|
|
120
|
+
* 同一条腿的两个载体,段必须来自同一只腿;在投帧那一刻读(core 推帧之前已 await 过 loader)。返回 `undefined` ⇒ 段缺席。 */
|
|
121
|
+
instructionsSegment: () => InstructionsSegment | undefined;
|
|
118
122
|
}): LedgerSink;
|
|
119
123
|
//# sourceMappingURL=ledger-sink.d.ts.map
|
|
@@ -97,7 +97,7 @@ export function createLedgerSink(opts) {
|
|
|
97
97
|
await append("workspace_changed", workspaceChangedEventData(ev));
|
|
98
98
|
break;
|
|
99
99
|
case "wiring_manifest":
|
|
100
|
-
await append("wiring_manifest", wiringManifestEventData(ev));
|
|
100
|
+
await append("wiring_manifest", wiringManifestEventData(ev, opts.instructionsSegment()));
|
|
101
101
|
break;
|
|
102
102
|
case "tool_roster_delta": {
|
|
103
103
|
const rosterDelta = toolRosterDeltaEventData(ev);
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ import type { RunEvent, RunRecord } from "../plugins/store-contracts.js";
|
|
|
28
28
|
import type { ModelUsageDelta, ModelUsageTracker, PromptManifestRecord } from "../budget.js";
|
|
29
29
|
import { type SemaProvenanceInput } from "./sema-provenance.js";
|
|
30
30
|
import type { LedgerEventType } from "./ledger-events.js";
|
|
31
|
+
import type { InstructionsSegment } from "../instruction-selection.js";
|
|
31
32
|
/** 账本写口的统一签名 —— `type` 是**闭集**(`ledger-events.ts`):新型必须先登记,否则调用点编译红
|
|
32
33
|
* (codex 交叉复审 F2:别名门 `ap("x")` 与动态首参让纯词法门结构性抓不全)。 */
|
|
33
34
|
type AppendFn = (type: LedgerEventType, data: unknown) => Promise<void>;
|
|
@@ -719,11 +720,18 @@ export declare function toolRosterDeltaEventData(ev: {
|
|
|
719
720
|
eventId?: unknown;
|
|
720
721
|
parentToolCallId?: unknown;
|
|
721
722
|
}): Record<string, unknown> | undefined;
|
|
723
|
+
/**
|
|
724
|
+
* 🔴 S-961(7.111.0)**第二参 `instructions` 必传**(值可以是 `undefined`):`wiring_manifest` 上唯一一段**本仓自铸**的段
|
|
725
|
+
* —— 这条腿的 project-memory loader 选中的指令文件链成员,`{ selected: string[] }`。它不在 core 的 manifest 里(core 不持有
|
|
726
|
+
* 选择规则),所以不从 `ev` 读:每条投帧的腿把**自己那只** `InstructionSelectionLeg.segment()` 递进来。必传而不是可选 ——
|
|
727
|
+
* 漏递的腿当场 tsc 红,而不是静默投出一个「缺席」(缺席在 wire 上是一句话:「这条腿没有关于指令文件的陈述」)。
|
|
728
|
+
* `undefined` ⇒ 段缺席;`{ selected: [] }` ⇒ 显式空(走了链、没选中)。登记:`wire-projection-faces.ts` 租户行 `minted`。
|
|
729
|
+
*/
|
|
722
730
|
export declare function wiringManifestEventData(ev: {
|
|
723
731
|
manifest?: unknown;
|
|
724
732
|
eventId?: unknown;
|
|
725
733
|
parentToolCallId?: unknown;
|
|
726
|
-
}): Record<string, unknown>;
|
|
734
|
+
}, instructions: InstructionsSegment | undefined): Record<string, unknown>;
|
|
727
735
|
/**
|
|
728
736
|
* `wiring_manifest` 的 **operator 投影**([ref] 件一)—— {@link wiringManifestEventData} 的租户形加上
|
|
729
737
|
* `governance` 段与 core 的真 `configFingerprint`。
|
|
@@ -759,7 +767,7 @@ export declare function wiringManifestOperatorEventData(ev: {
|
|
|
759
767
|
manifest?: unknown;
|
|
760
768
|
eventId?: unknown;
|
|
761
769
|
parentToolCallId?: unknown;
|
|
762
|
-
}): Record<string, unknown>;
|
|
770
|
+
}, instructions: InstructionsSegment | undefined): Record<string, unknown>;
|
|
763
771
|
/**
|
|
764
772
|
* `wiring_manifest` 的**唯一分叉点**(S-528)—— 两条知道调用方身份的 live 消费腿共用它:
|
|
765
773
|
* `/v1/tasks/stream` 的 SSE 帧,与 `POST /v1/tasks`(非流式)200 体里的 `wiringManifest` 起手回执。
|
|
@@ -780,7 +788,7 @@ export declare function wiringManifestFaceEventData(ev: {
|
|
|
780
788
|
manifest?: unknown;
|
|
781
789
|
eventId?: unknown;
|
|
782
790
|
parentToolCallId?: unknown;
|
|
783
|
-
}, operatorFace: boolean): Record<string, unknown>;
|
|
791
|
+
}, operatorFace: boolean, instructions: InstructionsSegment | undefined): Record<string, unknown>;
|
|
784
792
|
/**
|
|
785
793
|
* `human_input`(core 5.14.0 [ref] 新 `TaskEvent` 臂)—— 「谁把什么喂进了这条 run」的生命周期账本
|
|
786
794
|
* 帧(objective / 实时 steer / nextTurn / parked-steer 续跑 / wake 五个载体共用一个投影点)。
|
package/dist/trace/project.js
CHANGED
|
@@ -464,7 +464,7 @@ export function toolRosterDeltaEventData(ev) {
|
|
|
464
464
|
}
|
|
465
465
|
return { delta: ev.delta, ...identityFields(ev) };
|
|
466
466
|
}
|
|
467
|
-
export function wiringManifestEventData(ev) {
|
|
467
|
+
export function wiringManifestEventData(ev, instructions) {
|
|
468
468
|
const str = (v) => (typeof v === "string" && v.length > 0 ? v : undefined);
|
|
469
469
|
const bool = (v) => (typeof v === "boolean" ? v : undefined);
|
|
470
470
|
const num = (v) => (typeof v === "number" && Number.isFinite(v) ? v : undefined);
|
|
@@ -630,11 +630,12 @@ export function wiringManifestEventData(ev) {
|
|
|
630
630
|
...(lsp !== undefined ? { lsp } : {}),
|
|
631
631
|
...(hands !== undefined ? { hands } : {}),
|
|
632
632
|
...(writeProtection !== undefined ? { writeProtection } : {}),
|
|
633
|
+
...(instructions !== undefined ? { instructions: { selected: [...instructions.selected] } } : {}),
|
|
633
634
|
...identityFields(ev),
|
|
634
635
|
};
|
|
635
636
|
}
|
|
636
|
-
export function wiringManifestOperatorEventData(ev) {
|
|
637
|
-
const tenant = wiringManifestEventData(ev);
|
|
637
|
+
export function wiringManifestOperatorEventData(ev, instructions) {
|
|
638
|
+
const tenant = wiringManifestEventData(ev, instructions);
|
|
638
639
|
const str = (v) => (typeof v === "string" && v.length > 0 ? v : undefined);
|
|
639
640
|
const bool = (v) => (typeof v === "boolean" ? v : undefined);
|
|
640
641
|
const sec = (v) => (typeof v === "object" && v !== null && !Array.isArray(v) ? { ...v } : undefined);
|
|
@@ -678,8 +679,8 @@ export function wiringManifestOperatorEventData(ev) {
|
|
|
678
679
|
...(str(m.configFingerprint) !== undefined ? { configFingerprint: m.configFingerprint } : {}),
|
|
679
680
|
};
|
|
680
681
|
}
|
|
681
|
-
export function wiringManifestFaceEventData(ev, operatorFace) {
|
|
682
|
-
return operatorFace ? wiringManifestOperatorEventData(ev) : wiringManifestEventData(ev);
|
|
682
|
+
export function wiringManifestFaceEventData(ev, operatorFace, instructions) {
|
|
683
|
+
return operatorFace ? wiringManifestOperatorEventData(ev, instructions) : wiringManifestEventData(ev, instructions);
|
|
683
684
|
}
|
|
684
685
|
export function humanInputEventData(ev) {
|
|
685
686
|
const str = (v) => (typeof v === "string" && v.length > 0 ? v : undefined);
|
|
@@ -32,6 +32,32 @@ export declare const SELF_MINTED_WIRE: {
|
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* S-961(7.111.0):`wiring_manifest.instructions.selected[]` 的成员值 = 指令文件链的成员(相对 cwd 的拼写)。属主是 `project-memory.ts`
|
|
37
|
+
* 的 `INSTRUCTION_FILE_CHAIN`([ref] 序:CLAUDE 族先、AGENTS 族后,首中即选)—— 链加 / 删一员 ⇒ 本行 `satisfies` 编译红,登记跟着动。
|
|
38
|
+
* 段本身(键)登记在 `wire-projection-faces.ts` 的 `wiring_manifest_tenant.minted`。
|
|
39
|
+
*/
|
|
40
|
+
readonly wiringInstructionsSelected: {
|
|
41
|
+
readonly at: "wiring_manifest instructions.selected[](本腿 loader 选中的链成员;段缺席 = 本腿无陈述)";
|
|
42
|
+
readonly members: {
|
|
43
|
+
"CLAUDE.md": {
|
|
44
|
+
attr: string;
|
|
45
|
+
since: string;
|
|
46
|
+
};
|
|
47
|
+
".claude/CLAUDE.md": {
|
|
48
|
+
attr: string;
|
|
49
|
+
since: string;
|
|
50
|
+
};
|
|
51
|
+
"AGENTS.md": {
|
|
52
|
+
attr: string;
|
|
53
|
+
since: string;
|
|
54
|
+
};
|
|
55
|
+
".claude/AGENTS.md": {
|
|
56
|
+
attr: string;
|
|
57
|
+
since: string;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
};
|
|
35
61
|
/**
|
|
36
62
|
* 会话所有者自助抹除面的**已分类拒** → HTTP 状态(S-930 起与 operator 面 `POST /v1/memory/erase` 同一只映射;`since` 记的是**本面**
|
|
37
63
|
* 自哪一版起这样答 —— 7.107.0 及以前本面只认前两只,其余落 500 `internal.error`)。
|
|
@@ -12,6 +12,15 @@ export const SELF_MINTED_WIRE = {
|
|
|
12
12
|
crashed: { attr: "-", since: "7.110.0" },
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
|
+
wiringInstructionsSelected: {
|
|
16
|
+
at: "wiring_manifest instructions.selected[](本腿 loader 选中的链成员;段缺席 = 本腿无陈述)",
|
|
17
|
+
members: {
|
|
18
|
+
"CLAUDE.md": { attr: "-", since: "7.111.0" },
|
|
19
|
+
".claude/CLAUDE.md": { attr: "-", since: "7.111.0" },
|
|
20
|
+
"AGENTS.md": { attr: "-", since: "7.111.0" },
|
|
21
|
+
".claude/AGENTS.md": { attr: "-", since: "7.111.0" },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
15
24
|
sessionMemoryEraseStatus: {
|
|
16
25
|
at: "POST /v1/sessions/:id/memory/erase(已分类拒 errorCode → HTTP 状态)",
|
|
17
26
|
members: {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* `NotificationExcluded` 直接读成本表这一行的 `excluded`,于是「哪几个键有意不上 wire」全仓**只有一个写者**。
|
|
17
17
|
* 旧形是两个:守卫文件一簿、`test/wire-whitelist-exhaustiveness.test.ts` 的 `KnownExcluded` 一簿,那份门注
|
|
18
18
|
* 自己写着「两处必须同时改」—— 而「必须同时改」正是 [ref] 要下线的东西(对账门不算替代,单写者才算)。
|
|
19
|
-
* 本表是纯数据、无行为(唯一的派生是 `wiring_manifest_operator`
|
|
19
|
+
* 本表是纯数据、无行为(唯一的派生是 `wiring_manifest_operator` 行的两只取值器:`excluded` = 租户行减具名常量,S-724;`minted` = 租户行,S-961),
|
|
20
20
|
* 被守卫文件以 `typeof import(...)` 的**类型查询**读取(编译期擦除,零运行期边)。
|
|
21
21
|
*
|
|
22
22
|
* ## 门在哪里
|
|
@@ -170,14 +170,14 @@ export declare const WIRE_PROJECTION_FACES: {
|
|
|
170
170
|
readonly coreType: `WiringManifest & ${string}`;
|
|
171
171
|
readonly excluded: readonly ["governance", "configFingerprint", "autoConsolidation", "readDeny", "peerPresence", "permissionMode"];
|
|
172
172
|
readonly unwrapped: readonly ["manifest"];
|
|
173
|
-
readonly minted: readonly [];
|
|
173
|
+
readonly minted: readonly ["instructions"];
|
|
174
174
|
};
|
|
175
175
|
readonly wiring_manifest_operator: {
|
|
176
176
|
readonly projector: "wiringManifestOperatorEventData";
|
|
177
177
|
readonly coreType: `WiringManifest & ${string}`;
|
|
178
178
|
readonly excluded: readonly string[];
|
|
179
179
|
readonly unwrapped: readonly ["manifest"];
|
|
180
|
-
readonly minted: readonly [];
|
|
180
|
+
readonly minted: readonly string[];
|
|
181
181
|
};
|
|
182
182
|
readonly engine_notice: {
|
|
183
183
|
readonly projector: "engineNoticeEventData";
|