@te-river/opencode-team-mode 1.5.15 → 1.6.1
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 +853 -690
- package/README.zh-CN.md +761 -631
- package/dist/agents.d.ts +5 -3
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +19 -8
- package/dist/agents.js.map +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/capabilities.js +2 -2
- package/dist/capabilities.js.map +1 -1
- package/dist/host/note.d.ts +7 -0
- package/dist/host/note.d.ts.map +1 -0
- package/dist/host/note.js +28 -0
- package/dist/host/note.js.map +1 -0
- package/dist/host/v1.d.ts +14 -0
- package/dist/host/v1.d.ts.map +1 -0
- package/dist/host/v1.js +340 -0
- package/dist/host/v1.js.map +1 -0
- package/dist/host/v2-browser-gate.d.ts +45 -0
- package/dist/host/v2-browser-gate.d.ts.map +1 -0
- package/dist/host/v2-browser-gate.js +319 -0
- package/dist/host/v2-browser-gate.js.map +1 -0
- package/dist/host/v2-capabilities.d.ts +68 -0
- package/dist/host/v2-capabilities.d.ts.map +1 -0
- package/dist/host/v2-capabilities.js +146 -0
- package/dist/host/v2-capabilities.js.map +1 -0
- package/dist/host/v2-client.d.ts +53 -0
- package/dist/host/v2-client.d.ts.map +1 -0
- package/dist/host/v2-client.js +193 -0
- package/dist/host/v2-client.js.map +1 -0
- package/dist/host/v2-events.d.ts +56 -0
- package/dist/host/v2-events.d.ts.map +1 -0
- package/dist/host/v2-events.js +158 -0
- package/dist/host/v2-events.js.map +1 -0
- package/dist/host/v2-guard.d.ts +101 -0
- package/dist/host/v2-guard.d.ts.map +1 -0
- package/dist/host/v2-guard.js +199 -0
- package/dist/host/v2-guard.js.map +1 -0
- package/dist/host/v2-offload.d.ts +148 -0
- package/dist/host/v2-offload.d.ts.map +1 -0
- package/dist/host/v2-offload.js +288 -0
- package/dist/host/v2-offload.js.map +1 -0
- package/dist/host/v2-permissions.d.ts +123 -0
- package/dist/host/v2-permissions.d.ts.map +1 -0
- package/dist/host/v2-permissions.js +190 -0
- package/dist/host/v2-permissions.js.map +1 -0
- package/dist/host/v2-probe.d.ts +85 -0
- package/dist/host/v2-probe.d.ts.map +1 -0
- package/dist/host/v2-probe.js +268 -0
- package/dist/host/v2-probe.js.map +1 -0
- package/dist/host/v2-scope.d.ts +51 -0
- package/dist/host/v2-scope.d.ts.map +1 -0
- package/dist/host/v2-scope.js +58 -0
- package/dist/host/v2-scope.js.map +1 -0
- package/dist/host/v2-session-client.d.ts +74 -0
- package/dist/host/v2-session-client.d.ts.map +1 -0
- package/dist/host/v2-session-client.js +171 -0
- package/dist/host/v2-session-client.js.map +1 -0
- package/dist/host/v2-session.d.ts +85 -0
- package/dist/host/v2-session.d.ts.map +1 -0
- package/dist/host/v2-session.js +171 -0
- package/dist/host/v2-session.js.map +1 -0
- package/dist/host/v2-subagent.d.ts +157 -0
- package/dist/host/v2-subagent.d.ts.map +1 -0
- package/dist/host/v2-subagent.js +286 -0
- package/dist/host/v2-subagent.js.map +1 -0
- package/dist/host/v2-tool.d.ts +67 -0
- package/dist/host/v2-tool.d.ts.map +1 -0
- package/dist/host/v2-tool.js +209 -0
- package/dist/host/v2-tool.js.map +1 -0
- package/dist/host/v2-transcript.d.ts +73 -0
- package/dist/host/v2-transcript.d.ts.map +1 -0
- package/dist/host/v2-transcript.js +151 -0
- package/dist/host/v2-transcript.js.map +1 -0
- package/dist/host/v2-types.d.ts +229 -0
- package/dist/host/v2-types.d.ts.map +1 -0
- package/dist/host/v2-types.js +17 -0
- package/dist/host/v2-types.js.map +1 -0
- package/dist/host/v2.d.ts +42 -0
- package/dist/host/v2.d.ts.map +1 -0
- package/dist/host/v2.js +706 -0
- package/dist/host/v2.js.map +1 -0
- package/dist/host-hooks.d.ts +13 -2
- package/dist/host-hooks.d.ts.map +1 -1
- package/dist/host-hooks.js +18 -5
- package/dist/host-hooks.js.map +1 -1
- package/dist/index.d.ts +23 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -326
- package/dist/index.js.map +1 -1
- package/dist/prompts/lead.d.ts +1 -1
- package/dist/prompts/lead.d.ts.map +1 -1
- package/dist/prompts/lead.js +452 -359
- package/dist/prompts/lead.js.map +1 -1
- package/dist/prompts/shared.d.ts +2 -2
- package/dist/prompts/shared.d.ts.map +1 -1
- package/dist/prompts/shared.js +89 -17
- package/dist/prompts/shared.js.map +1 -1
- package/dist/prompts/specialists.d.ts +2 -2
- package/dist/prompts/specialists.d.ts.map +1 -1
- package/dist/prompts/specialists.js +44 -10
- package/dist/prompts/specialists.js.map +1 -1
- package/dist/task-offload.d.ts +83 -0
- package/dist/task-offload.d.ts.map +1 -0
- package/dist/task-offload.js +174 -0
- package/dist/task-offload.js.map +1 -0
- package/dist/tm/args-schema.d.ts +4 -0
- package/dist/tm/args-schema.d.ts.map +1 -1
- package/dist/tm/args-schema.js +36 -3
- package/dist/tm/args-schema.js.map +1 -1
- package/dist/tm/board.d.ts +113 -0
- package/dist/tm/board.d.ts.map +1 -0
- package/dist/tm/board.js +275 -0
- package/dist/tm/board.js.map +1 -0
- package/dist/tm/browser.d.ts +325 -6
- package/dist/tm/browser.d.ts.map +1 -1
- package/dist/tm/browser.js +1360 -135
- package/dist/tm/browser.js.map +1 -1
- package/dist/tm/config.d.ts +36 -25
- package/dist/tm/config.d.ts.map +1 -1
- package/dist/tm/config.js +29 -7
- package/dist/tm/config.js.map +1 -1
- package/dist/tm/dispatch.d.ts +133 -53
- package/dist/tm/dispatch.d.ts.map +1 -1
- package/dist/tm/dispatch.js +481 -279
- package/dist/tm/dispatch.js.map +1 -1
- package/dist/tm/dupe-guard.d.ts +16 -3
- package/dist/tm/dupe-guard.d.ts.map +1 -1
- package/dist/tm/dupe-guard.js +27 -7
- package/dist/tm/dupe-guard.js.map +1 -1
- package/dist/tm/egress.d.ts +44 -0
- package/dist/tm/egress.d.ts.map +1 -0
- package/dist/tm/egress.js +191 -0
- package/dist/tm/egress.js.map +1 -0
- package/dist/tm/guard.js +1 -1
- package/dist/tm/guard.js.map +1 -1
- package/dist/tm/index.d.ts +67 -2
- package/dist/tm/index.d.ts.map +1 -1
- package/dist/tm/index.js +228 -20
- package/dist/tm/index.js.map +1 -1
- package/dist/tm/ledger.d.ts +114 -0
- package/dist/tm/ledger.d.ts.map +1 -0
- package/dist/tm/ledger.js +308 -0
- package/dist/tm/ledger.js.map +1 -0
- package/dist/tm/perm-ask.d.ts +45 -5
- package/dist/tm/perm-ask.d.ts.map +1 -1
- package/dist/tm/perm-ask.js +107 -7
- package/dist/tm/perm-ask.js.map +1 -1
- package/dist/tm/pipelines.d.ts.map +1 -1
- package/dist/tm/pipelines.js +24 -2
- package/dist/tm/pipelines.js.map +1 -1
- package/dist/tm/preview.d.ts +55 -0
- package/dist/tm/preview.d.ts.map +1 -1
- package/dist/tm/preview.js +225 -0
- package/dist/tm/preview.js.map +1 -1
- package/dist/tm/ptc/tool.d.ts.map +1 -1
- package/dist/tm/ptc/tool.js +2 -4
- package/dist/tm/ptc/tool.js.map +1 -1
- package/dist/tm/pty.d.ts.map +1 -1
- package/dist/tm/pty.js +6 -3
- package/dist/tm/pty.js.map +1 -1
- package/dist/tm/search.d.ts +5 -0
- package/dist/tm/search.d.ts.map +1 -1
- package/dist/tm/search.js +40 -8
- package/dist/tm/search.js.map +1 -1
- package/dist/tm/serp-loop.d.ts +45 -0
- package/dist/tm/serp-loop.d.ts.map +1 -0
- package/dist/tm/serp-loop.js +80 -0
- package/dist/tm/serp-loop.js.map +1 -0
- package/dist/tm/stats.d.ts +26 -0
- package/dist/tm/stats.d.ts.map +1 -1
- package/dist/tm/stats.js +194 -4
- package/dist/tm/stats.js.map +1 -1
- package/dist/tm/webfetch.d.ts +56 -0
- package/dist/tm/webfetch.d.ts.map +1 -1
- package/dist/tm/webfetch.js +187 -19
- package/dist/tm/webfetch.js.map +1 -1
- package/dist/tool-coerce.d.ts +24 -0
- package/dist/tool-coerce.d.ts.map +1 -0
- package/dist/tool-coerce.js +63 -0
- package/dist/tool-coerce.js.map +1 -0
- package/dist/types.d.ts +7 -1
- package/dist/types.d.ts.map +1 -1
- package/docs/installation-v2.md +436 -0
- package/docs/installation.md +81 -2
- package/docs/research/agent-data-exchange.md +117 -0
- package/docs/research/browser-pane.md +89 -0
- package/docs/research/host-http-api.md +132 -0
- package/docs/research/host-subagent-injection.md +187 -0
- package/docs/research/plugin-loader-contract.md +261 -0
- package/docs/research/v2-builtin-tools.md +447 -0
- package/docs/v2-port-notes.md +315 -0
- package/index.js +7 -0
- package/package.json +4 -1
- package/scripts/gen-v2-config.mjs +304 -0
- package/scripts/lib/config-surgery.cjs +239 -0
- package/scripts/lib/fake-ctx.mjs +146 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tm_ledger` — the LEDGER rule's landing spot on a host that has no `todowrite`.
|
|
3
|
+
*
|
|
4
|
+
* The lead's prompt mandates it: every new ask becomes a list item BEFORE the
|
|
5
|
+
* work, an interruption is an insertion rather than a replacement, `blocked` is a
|
|
6
|
+
* state and not an exit, and after a compaction the list is re-read before
|
|
7
|
+
* anything else. On v1 that list is the host's own `todowrite`. On v2 a plugin
|
|
8
|
+
* cannot create that tool (`V2_ONLY_ACTIONS` names `todowrite` as a key with no
|
|
9
|
+
* v2 counterpart), so the mandate had no落点 — and the goal tripwire in `tm_join`
|
|
10
|
+
* had nothing to check, which it now says out loud (`goal_unchecked reason=no_seam`).
|
|
11
|
+
*
|
|
12
|
+
* This module is the other answer. Storage is `ctx.storage` — the host's own
|
|
13
|
+
* key/value domain, proven by a boot self-check that writes a marker and reads it
|
|
14
|
+
* back. That choice beats the alternative (a file under the run store), which is
|
|
15
|
+
* the user's work-in-progress sitting in a temp directory with no owner. Two facts
|
|
16
|
+
* from a live 2.0.16 probe (`docs/research/agent-data-exchange.md`) shape this file:
|
|
17
|
+
* · the domain is shared across sessions, agents AND projects — the only namespace
|
|
18
|
+
* is the plugin id — so the session id is part of the key and nothing may be
|
|
19
|
+
* written without one;
|
|
20
|
+
* · there is no TTL and no quota, so a list that only ever grows is a leak with a
|
|
21
|
+
* friendly name. That is why `LEDGER_MAX_ITEMS` refuses instead of promising a
|
|
22
|
+
* cleanup mechanism this host does not offer.
|
|
23
|
+
*
|
|
24
|
+
* The honesty rule this module exists to obey (product goal #6): a reply about
|
|
25
|
+
* the list says whether the list REACHED storage. An in-memory-only "已记录"
|
|
26
|
+
* would be exactly the unverified "done" the rest of this plugin treats as a bug.
|
|
27
|
+
*/
|
|
28
|
+
import { tmError, toToolResult } from "./result.js";
|
|
29
|
+
export const LEDGER_STATUSES = ["open", "doing", "done", "blocked"];
|
|
30
|
+
const KEY_PREFIX = "team-mode/ledger/";
|
|
31
|
+
/** `ctx.storage.get` answered with the raw value in one build and with
|
|
32
|
+
* `{value: …}` in another (the boot self-check accepts both), so the reader has
|
|
33
|
+
* to unwrap both rather than trusting either. */
|
|
34
|
+
function unwrapStored(raw) {
|
|
35
|
+
if (raw && typeof raw === "object" && "value" in raw) {
|
|
36
|
+
return raw.value;
|
|
37
|
+
}
|
|
38
|
+
return raw;
|
|
39
|
+
}
|
|
40
|
+
export function createStorageLedgerStore(storage) {
|
|
41
|
+
const st = storage;
|
|
42
|
+
const ok = !!st && typeof st.get === "function" && typeof st.set === "function";
|
|
43
|
+
let detail;
|
|
44
|
+
return {
|
|
45
|
+
kind: "ctx.storage",
|
|
46
|
+
available: () => ok,
|
|
47
|
+
detail: () => detail ?? "",
|
|
48
|
+
async load(sessionID) {
|
|
49
|
+
if (!ok)
|
|
50
|
+
throw new Error("这个宿主没有给 ctx.storage 的 get/set");
|
|
51
|
+
const raw = await st.get(KEY_PREFIX + sessionID);
|
|
52
|
+
return normalizeLedger(unwrapStored(raw));
|
|
53
|
+
},
|
|
54
|
+
async save(ledger) {
|
|
55
|
+
if (!ok)
|
|
56
|
+
throw new Error("这个宿主没有给 ctx.storage 的 get/set");
|
|
57
|
+
try {
|
|
58
|
+
await st.set(KEY_PREFIX + ledger.sessionID, ledger);
|
|
59
|
+
detail = undefined;
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
detail = String(err?.message ?? err).slice(0, 120);
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Defensive read: a value that is not a ledger is treated as absent rather than
|
|
69
|
+
* half-trusted, because a silently-mangled list is worse than an empty one —
|
|
70
|
+
* the lead would wrap up believing the work was accounted for. */
|
|
71
|
+
export function normalizeLedger(raw) {
|
|
72
|
+
if (!raw || typeof raw !== "object")
|
|
73
|
+
return null;
|
|
74
|
+
const o = raw;
|
|
75
|
+
if (!Array.isArray(o.items))
|
|
76
|
+
return null;
|
|
77
|
+
const items = [];
|
|
78
|
+
for (const it of o.items) {
|
|
79
|
+
if (!it || typeof it !== "object")
|
|
80
|
+
continue;
|
|
81
|
+
const e = it;
|
|
82
|
+
const text = typeof e.text === "string" ? e.text.trim() : "";
|
|
83
|
+
if (!text)
|
|
84
|
+
continue;
|
|
85
|
+
items.push({
|
|
86
|
+
id: Number.isFinite(Number(e.id)) ? Number(e.id) : items.length + 1,
|
|
87
|
+
text,
|
|
88
|
+
status: LEDGER_STATUSES.includes(String(e.status))
|
|
89
|
+
? String(e.status)
|
|
90
|
+
: "open",
|
|
91
|
+
at: Number.isFinite(Number(e.at)) ? Number(e.at) : Date.now(),
|
|
92
|
+
...(typeof e.note === "string" && e.note ? { note: e.note } : {}),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
sessionID: String(o.sessionID ?? ""),
|
|
97
|
+
items,
|
|
98
|
+
updated: Number.isFinite(Number(o.updated)) ? Number(o.updated) : Date.now(),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** The dedup key. The LEDGER rule says an interruption INSERTS, and the same ask
|
|
102
|
+
* arriving twice (a re-stated instruction, a screenshot of the same question)
|
|
103
|
+
* must not grow a second line the lead then has to close twice — so the key is
|
|
104
|
+
* the text with case/whitespace/trailing punctuation removed, which is the same
|
|
105
|
+
* "same question" notion tm_memory's dedupKey uses. */
|
|
106
|
+
export function ledgerDedupKey(text) {
|
|
107
|
+
return String(text ?? "")
|
|
108
|
+
.toLowerCase()
|
|
109
|
+
.replace(/[\s ]+/g, " ")
|
|
110
|
+
.replace(/[。..!!??;;::,,]+$/g, "")
|
|
111
|
+
.trim();
|
|
112
|
+
}
|
|
113
|
+
export function emptyLedger(sessionID) {
|
|
114
|
+
return { sessionID, items: [], updated: Date.now() };
|
|
115
|
+
}
|
|
116
|
+
/** `ctx.storage` never expires anything (measured), so the list needs a ceiling or
|
|
117
|
+
* it grows for the life of the install. Refusing is honest; silently truncating
|
|
118
|
+
* would be the overstated claim — the lead would believe its oldest asks were gone
|
|
119
|
+
* when they were only hidden. */
|
|
120
|
+
export const LEDGER_MAX_ITEMS_DEFAULT = 200;
|
|
121
|
+
export function ledgerMaxItems(env = process.env) {
|
|
122
|
+
const n = Number(String(env.TM_LEDGER_MAX_ITEMS ?? "").trim());
|
|
123
|
+
return Number.isFinite(n) && n >= 10 ? Math.floor(n) : LEDGER_MAX_ITEMS_DEFAULT;
|
|
124
|
+
}
|
|
125
|
+
/** A merge is reported, not hidden: the reply says "这条已经在清单上(#2)" so the
|
|
126
|
+
* lead learns the insertion was a repeat instead of wondering why the count did
|
|
127
|
+
* not move. */
|
|
128
|
+
export function addItem(ledger, text, now = Date.now()) {
|
|
129
|
+
const clean = String(text ?? "").replace(/[\s ]+/g, " ").trim().slice(0, 400);
|
|
130
|
+
const key = ledgerDedupKey(clean);
|
|
131
|
+
const existing = clean ? ledger.items.find((i) => ledgerDedupKey(i.text) === key) : undefined;
|
|
132
|
+
if (existing)
|
|
133
|
+
return { ledger, item: existing, merged: true };
|
|
134
|
+
const nextId = ledger.items.reduce((m, i) => Math.max(m, i.id), 0) + 1;
|
|
135
|
+
const item = { id: nextId, text: clean, status: "open", at: now };
|
|
136
|
+
return { ledger: { ...ledger, items: [...ledger.items, item], updated: now }, item, merged: false };
|
|
137
|
+
}
|
|
138
|
+
const MARKS = { open: "[ ]", doing: "[>]", done: "[x]", blocked: "[!]" };
|
|
139
|
+
export function openItems(ledger) {
|
|
140
|
+
return (ledger?.items ?? []).filter((i) => i.status !== "done");
|
|
141
|
+
}
|
|
142
|
+
/** Resolve `#4` / `4` / a substring, and refuse an ambiguous match by NAMING the
|
|
143
|
+
* candidates — a list tool that silently marks the wrong line destroys the one
|
|
144
|
+
* thing it is for. */
|
|
145
|
+
export function findItem(ledger, ref) {
|
|
146
|
+
const s = String(ref ?? "").trim().replace(/^#/, "");
|
|
147
|
+
if (!s)
|
|
148
|
+
return { candidates: ledger.items };
|
|
149
|
+
if (/^\d+$/.test(s)) {
|
|
150
|
+
const byId = ledger.items.find((i) => i.id === Number(s));
|
|
151
|
+
return byId ? { item: byId, candidates: [byId] } : { candidates: [] };
|
|
152
|
+
}
|
|
153
|
+
const key = ledgerDedupKey(s);
|
|
154
|
+
const hits = ledger.items.filter((i) => ledgerDedupKey(i.text).includes(key) || key.includes(ledgerDedupKey(i.text)));
|
|
155
|
+
return hits.length === 1 ? { item: hits[0], candidates: hits } : { candidates: hits };
|
|
156
|
+
}
|
|
157
|
+
export function markItem(ledger, ref, status, note, now = Date.now()) {
|
|
158
|
+
const { item, candidates } = findItem(ledger, ref);
|
|
159
|
+
if (!item) {
|
|
160
|
+
const named = candidates.slice(0, 4).map((c) => `#${c.id} ${c.text}`).join("、");
|
|
161
|
+
return {
|
|
162
|
+
error: candidates.length
|
|
163
|
+
? `「${ref}」匹配到 ${candidates.length} 条,不能猜:${named} —— 用编号重发一次`
|
|
164
|
+
: `清单里没有「${ref}」这一条(现有 ${ledger.items.length} 条)—— 先 tm_ledger { action:"list" } 看一眼`,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const next = {
|
|
168
|
+
...item,
|
|
169
|
+
status,
|
|
170
|
+
at: now,
|
|
171
|
+
...(String(note ?? "").trim() ? { note: String(note).trim().slice(0, 400) } : {}),
|
|
172
|
+
};
|
|
173
|
+
return { ledger: { ...ledger, items: ledger.items.map((i) => (i.id === item.id ? next : i)), updated: now }, item: next };
|
|
174
|
+
}
|
|
175
|
+
export function renderLedger(ledger) {
|
|
176
|
+
const open = openItems(ledger);
|
|
177
|
+
const done = ledger.items.length - open.length;
|
|
178
|
+
const head = `清单 ${ledger.sessionID}:共 ${ledger.items.length} 条 · 未完成 ${open.length} · 已完成 ${done}`;
|
|
179
|
+
if (!ledger.items.length)
|
|
180
|
+
return `${head}\n(空 —— 每接一个新要求就先加一条,再动手)`;
|
|
181
|
+
const lines = ledger.items.map((i) => {
|
|
182
|
+
const note = i.note ? ` —— ${i.note}` : "";
|
|
183
|
+
return `- ${MARKS[i.status]} #${i.id} ${i.text}${note}`;
|
|
184
|
+
});
|
|
185
|
+
return [head, ...lines].join("\n");
|
|
186
|
+
}
|
|
187
|
+
/** The line `tm_join` appends when the round settled but the LEDGER did not —
|
|
188
|
+
* the goal tripwire's shape applied to a list the plugin actually owns. */
|
|
189
|
+
export function ledgerGoalLine(ledger, max = 6) {
|
|
190
|
+
const open = openItems(ledger);
|
|
191
|
+
if (!open.length)
|
|
192
|
+
return null;
|
|
193
|
+
const named = open
|
|
194
|
+
.slice(0, max)
|
|
195
|
+
.map((i) => `「${i.text}」(${i.status === "blocked" ? "卡住" : i.status === "doing" ? "进行中" : "未开始"})`)
|
|
196
|
+
.join("、");
|
|
197
|
+
return (`⚠ 目标未达成:tm_ledger 还有 ${open.length} 项未完成 —— ${named}` +
|
|
198
|
+
(open.length > max ? ` …+${open.length - max}` : "") +
|
|
199
|
+
`\n按目标指令:要么继续做掉,要么向用户写明哪一条被什么卡住;不要把这轮当成收尾。` +
|
|
200
|
+
`做完了就 tm_ledger { action:"done", id:#编号 } 更新状态,再收尾。`);
|
|
201
|
+
}
|
|
202
|
+
const DESCRIPTION = `你自己的任务清单(LEDGER)——这个宿主没有 todowrite,清单由本插件记在宿主的 ctx.storage 里。
|
|
203
|
+
- { action:"add", text:"…" }:接一个新要求就先加一条,再加进清单之后的活(中途插入的要求同样先加,别把原清单换掉)。同样的话说过两遍会告诉你它已经在第几条,不重复加。
|
|
204
|
+
- { action:"doing" | "done" | "blocked", id:"#编号 或一句话" }:改状态。blocked 是一个状态,不是收工的理由——note 里写清被什么卡住(这是别人复述不出来的那半句)。
|
|
205
|
+
- { action:"list" }:把整张清单读回来。上下文被压缩过、或者你不记得走到哪儿了,先读这个,别凭印象继续。
|
|
206
|
+
- 编号在答复里一定会回给你;id 用编号最准,用一句话的片段也行,但撞上两条会拒绝并把候选列给你,不会替你猜。
|
|
207
|
+
- 答复里会说明这条清单是否真的进了 ctx.storage:没进去就是失败,不会算"已记录"。`;
|
|
208
|
+
const LEDGER_ARGS = {
|
|
209
|
+
action: { descriptor: 'action: "add" | "doing" | "done" | "blocked" | "list"' },
|
|
210
|
+
text: { descriptor: "text: string (add 用:一条要求的原文,越短越好)" },
|
|
211
|
+
id: { descriptor: 'id: number|string (改状态用:清单编号,或者能唯一对上的一条的片段)' },
|
|
212
|
+
note: { descriptor: "note: string (可选:blocked 被什么卡住 / done 凭什么判定完成)" },
|
|
213
|
+
};
|
|
214
|
+
export function buildLedgerTool(deps) {
|
|
215
|
+
const tool = "tm_ledger";
|
|
216
|
+
const now = deps.now ?? (() => Date.now());
|
|
217
|
+
return {
|
|
218
|
+
description: DESCRIPTION,
|
|
219
|
+
args: LEDGER_ARGS,
|
|
220
|
+
execute: async (rawArgs, ctx) => {
|
|
221
|
+
const c = (ctx ?? {});
|
|
222
|
+
if (deps.onlyAgent && String(c.agent ?? "") !== deps.onlyAgent) {
|
|
223
|
+
return toToolResult(tmError(tool, "permission", `tm_ledger 只有领队能用(现在的调用方是「${String(c.agent || "未知")}」)。` +
|
|
224
|
+
`专家的状态写在 STATUS 里,由领队并进它的那张清单——不要另起一张。`));
|
|
225
|
+
}
|
|
226
|
+
const args = (rawArgs ?? {});
|
|
227
|
+
const sessionID = String(c.sessionID ?? "").trim();
|
|
228
|
+
if (!sessionID) {
|
|
229
|
+
return toToolResult(tmError(tool, "args", "这个调用没有带 sessionID —— 清单是按会话存的,不能凭空写到一个未知会话上。"));
|
|
230
|
+
}
|
|
231
|
+
if (!deps.store.available()) {
|
|
232
|
+
return toToolResult(tmError(tool, "client", `这个宿主没有给出可写的 ctx.storage(${deps.store.kind}),所以清单没地方落。` +
|
|
233
|
+
`不要把这件事当成"清单功能坏了":把状态写进 STATUS/CHANGES 里,照样可核对。`));
|
|
234
|
+
}
|
|
235
|
+
let ledger;
|
|
236
|
+
try {
|
|
237
|
+
ledger = (await deps.store.load(sessionID)) ?? emptyLedger(sessionID);
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
return toToolResult(tmError(tool, "execute", `读清单失败(${deps.store.kind}):${String(err?.message ?? err).slice(0, 160)}`));
|
|
241
|
+
}
|
|
242
|
+
const action = String(args.action ?? "").trim().toLowerCase();
|
|
243
|
+
if (!["add", "doing", "done", "blocked", "list"].includes(action)) {
|
|
244
|
+
return toToolResult(tmError(tool, "args", `action 只能是 add / doing / done / blocked / list,收到「${action || "(空)"}」`));
|
|
245
|
+
}
|
|
246
|
+
if (action === "list") {
|
|
247
|
+
return toToolResult({ ok: true, output: renderLedger(ledger), meta: { open: openItems(ledger).length } });
|
|
248
|
+
}
|
|
249
|
+
let answer = "";
|
|
250
|
+
if (action === "add") {
|
|
251
|
+
const texts = Array.isArray(args.text)
|
|
252
|
+
? args.text.map(String)
|
|
253
|
+
: String(args.text ?? "")
|
|
254
|
+
.split(/\r?\n|;/)
|
|
255
|
+
.map((s) => s.trim())
|
|
256
|
+
.filter(Boolean);
|
|
257
|
+
if (!texts.length) {
|
|
258
|
+
return toToolResult(tmError(tool, "args", "add 需要 text —— 空的一条要求只会让清单变长,不会让活变少"));
|
|
259
|
+
}
|
|
260
|
+
const max = ledgerMaxItems(deps.env);
|
|
261
|
+
if (ledger.items.length + texts.length > max) {
|
|
262
|
+
return toToolResult(tmError(tool, "args", `清单已有 ${ledger.items.length} 条,再加 ${texts.length} 条会超过上限 ${max}(TM_LEDGER_MAX_ITEMS)。` +
|
|
263
|
+
`宿主的 ctx.storage 不设 TTL,也不会替你回收:先把已完成的条目在回复里总结掉,或显式清一次,别让它无声涨下去。`));
|
|
264
|
+
}
|
|
265
|
+
let merged = 0;
|
|
266
|
+
const added = [];
|
|
267
|
+
for (const t of texts) {
|
|
268
|
+
const r = addItem(ledger, t, now());
|
|
269
|
+
ledger = r.ledger;
|
|
270
|
+
if (r.merged)
|
|
271
|
+
merged++;
|
|
272
|
+
else
|
|
273
|
+
added.push(r.item);
|
|
274
|
+
}
|
|
275
|
+
answer = added.length
|
|
276
|
+
? `已记 ${added.map((i) => `#${i.id} ${i.text}`).join(";")}${merged ? `(另有 ${merged} 条重复,没再加)` : ""}`
|
|
277
|
+
: `这 ${merged} 条已经在清单上了,没重复加:${texts.map((t) => { const f = findItem(ledger, t); return f.item ? `#${f.item.id}(${f.item.status})` : "?"; }).join("、")}`;
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
const ref = (args.id ?? args.text);
|
|
281
|
+
if (ref === undefined || String(ref).trim() === "") {
|
|
282
|
+
return toToolResult(tmError(tool, "args", `${action} 需要 id —— 编号,或者能唯一对上的一条的片段`));
|
|
283
|
+
}
|
|
284
|
+
const r = markItem(ledger, ref, action, typeof args.note === "string" ? args.note : undefined, now());
|
|
285
|
+
if ("error" in r)
|
|
286
|
+
return toToolResult(tmError(tool, "args", r.error));
|
|
287
|
+
ledger = r.ledger;
|
|
288
|
+
answer = `#${r.item.id} → ${action}:${r.item.text}${r.item.note ? `(${r.item.note})` : ""}`;
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
await deps.store.save(ledger);
|
|
292
|
+
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
// The change did not land. Saying 已记录 here would be the unverified
|
|
295
|
+
// "done" this product treats as its own defect class.
|
|
296
|
+
return toToolResult(tmError(tool, "execute", `${answer}\n但这次改动没写进 ${deps.store.kind}:${String(err?.message ?? err).slice(0, 160)} —— 清单还是上一次的样子。`));
|
|
297
|
+
}
|
|
298
|
+
deps.onWrite?.(sessionID, action);
|
|
299
|
+
const rest = openItems(ledger);
|
|
300
|
+
return toToolResult({
|
|
301
|
+
ok: true,
|
|
302
|
+
output: `${answer}\n(已写入 ${deps.store.kind} · 未完成 ${rest.length} 条${rest.length ? `:${rest.slice(0, 6).map((i) => `#${i.id}`).join(" ")}` : ""})`,
|
|
303
|
+
meta: { open: rest.length },
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/tm/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEnD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAU,CAAA;AAgC5E,MAAM,UAAU,GAAG,mBAAmB,CAAA;AAEtC;;kDAEkD;AAClD,SAAS,YAAY,CAAC,GAAY;IAChC,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,IAAK,GAA+B,EAAE,CAAC;QAClF,OAAQ,GAA0B,CAAC,KAAK,CAAA;IAC1C,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACvD,MAAM,EAAE,GAAG,OAA8D,CAAA;IACzE,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,OAAO,EAAE,CAAC,GAAG,KAAK,UAAU,IAAI,OAAO,EAAE,CAAC,GAAG,KAAK,UAAU,CAAA;IAC/E,IAAI,MAA0B,CAAA;IAC9B,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE;QACnB,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,IAAI,EAAE;QAC1B,KAAK,CAAC,IAAI,CAAC,SAAS;YAClB,IAAI,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;YACzD,MAAM,GAAG,GAAG,MAAO,EAAsC,CAAC,GAAG,CAAC,UAAU,GAAG,SAAS,CAAC,CAAA;YACrF,OAAO,eAAe,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAA;QAC3C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,MAAM;YACf,IAAI,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;YACzD,IAAI,CAAC;gBACH,MAAO,EAAkD,CAAC,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;gBACpG,MAAM,GAAG,SAAS,CAAA;YACpB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,GAAG,MAAM,CAAE,GAAa,EAAE,OAAO,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;gBAC7D,MAAM,GAAG,CAAA;YACX,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED;;mEAEmE;AACnE,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAChD,MAAM,CAAC,GAAG,GAAkE,CAAA;IAC5E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACxC,MAAM,KAAK,GAAiB,EAAE,CAAA;IAC9B,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,EAAE,IAAI,OAAO,EAAE,KAAK,QAAQ;YAAE,SAAQ;QAC3C,MAAM,CAAC,GAAG,EAA6B,CAAA;QACvC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5D,IAAI,CAAC,IAAI;YAAE,SAAQ;QACnB,KAAK,CAAC,IAAI,CAAC;YACT,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;YACnE,IAAI;YACJ,MAAM,EAAG,eAAqC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;gBACvE,CAAC,CAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAkB;gBACpC,CAAC,CAAC,MAAM;YACV,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;YAC7D,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClE,CAAC,CAAA;IACJ,CAAC;IACD,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC;QACpC,KAAK;QACL,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;KAC7E,CAAA;AACH,CAAC;AAED;;;;wDAIwD;AACxD,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;SACtB,WAAW,EAAE;SACb,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC;SACjC,IAAI,EAAE,CAAA;AACX,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;AACtD,CAAC;AAED;;;kCAGkC;AAClC,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAA;AAE3C,MAAM,UAAU,cAAc,CAAC,MAA0C,OAAO,CAAC,GAAG;IAClF,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IAC9D,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB,CAAA;AACjF,CAAC;AAED;;gBAEgB;AAChB,MAAM,UAAU,OAAO,CACrB,MAAc,EACd,IAAY,EACZ,MAAc,IAAI,CAAC,GAAG,EAAE;IAExB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC7E,MAAM,GAAG,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;IACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC7F,IAAI,QAAQ;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IAC7D,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;IACtE,MAAM,IAAI,GAAe,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,CAAA;IAC7E,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AACrG,CAAC;AAED,MAAM,KAAK,GAAiC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;AAEtG,MAAM,UAAU,SAAS,CAAC,MAAiC;IACzD,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAA;AACjE,CAAC;AAED;;uBAEuB;AACvB,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,GAAoB;IAC3D,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IACpD,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,KAAK,EAAE,CAAA;IAC3C,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QACzD,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAA;IACvE,CAAC;IACD,MAAM,GAAG,GAAG,cAAc,CAAC,CAAC,CAAC,CAAA;IAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CACpF,CAAA;IACD,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;AACvF,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,MAAc,EACd,GAAoB,EACpB,MAAoB,EACpB,IAAa,EACb,MAAc,IAAI,CAAC,GAAG,EAAE;IAExB,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAClD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC/E,OAAO;YACL,KAAK,EAAE,UAAU,CAAC,MAAM;gBACtB,CAAC,CAAC,IAAI,GAAG,QAAQ,UAAU,CAAC,MAAM,UAAU,KAAK,aAAa;gBAC9D,CAAC,CAAC,SAAS,GAAG,WAAW,MAAM,CAAC,KAAK,CAAC,MAAM,yCAAyC;SACxF,CAAA;IACH,CAAC;IACD,MAAM,IAAI,GAAe;QACvB,GAAG,IAAI;QACP,MAAM;QACN,EAAE,EAAE,GAAG;QACP,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClF,CAAA;IACD,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;AAC3H,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;IAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;IAC9C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,SAAS,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,YAAY,IAAI,CAAC,MAAM,UAAU,IAAI,EAAE,CAAA;IACnG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,GAAG,IAAI,2BAA2B,CAAA;IACnE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACnC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QAC1C,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,GAAG,IAAI,EAAE,CAAA;IACzD,CAAC,CAAC,CAAA;IACF,OAAO,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACpC,CAAC;AAED;4EAC4E;AAC5E,MAAM,UAAU,cAAc,CAAC,MAAiC,EAAE,GAAG,GAAG,CAAC;IACvE,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;IAC9B,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAC7B,MAAM,KAAK,GAAG,IAAI;SACf,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;SACb,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;SAClG,IAAI,CAAC,GAAG,CAAC,CAAA;IACZ,OAAO,CACL,wBAAwB,IAAI,CAAC,MAAM,YAAY,KAAK,EAAE;QACtD,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,2CAA2C;QAC3C,oDAAoD,CACrD,CAAA;AACH,CAAC;AAED,MAAM,WAAW,GAAG;;;;;iDAK6B,CAAA;AAEjD,MAAM,WAAW,GAAG;IAClB,MAAM,EAAE,EAAE,UAAU,EAAE,uDAAuD,EAAE;IAC/E,IAAI,EAAE,EAAE,UAAU,EAAE,mCAAmC,EAAE;IACzD,EAAE,EAAE,EAAE,UAAU,EAAE,6CAA6C,EAAE;IACjE,IAAI,EAAE,EAAE,UAAU,EAAE,gDAAgD,EAAE;CACvE,CAAA;AAeD,MAAM,UAAU,eAAe,CAAC,IAAoB;IAClD,MAAM,IAAI,GAAG,WAAW,CAAA;IACxB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IAC1C,OAAO;QACL,WAAW,EAAE,WAAW;QACxB,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAuB,EAAE;YACnD,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAA6C,CAAA;YACjE,IAAI,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC/D,OAAO,YAAY,CACjB,OAAO,CACL,IAAI,EACJ,YAAY,EACZ,4BAA4B,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK;oBACtD,uCAAuC,CAC1C,CACF,CAAA;YACH,CAAC;YACD,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAA4B,CAAA;YACvD,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;YAClD,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,YAAY,CACjB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,8CAA8C,CAAC,CACtE,CAAA;YACH,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC;gBAC5B,OAAO,YAAY,CACjB,OAAO,CACL,IAAI,EACJ,QAAQ,EACR,2BAA2B,IAAI,CAAC,KAAK,CAAC,IAAI,aAAa;oBACrD,gDAAgD,CACnD,CACF,CAAA;YACH,CAAC;YACD,IAAI,MAAc,CAAA;YAClB,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,WAAW,CAAC,SAAS,CAAC,CAAA;YACvE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,YAAY,CACjB,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAE,GAAa,EAAE,OAAO,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAC9G,CAAA;YACH,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;YAC7D,IAAI,CAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAuB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzF,OAAO,YAAY,CACjB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,qDAAqD,MAAM,IAAI,KAAK,GAAG,CAAC,CAC/F,CAAA;YACH,CAAC;YAED,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACtB,OAAO,YAAY,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;YAC3G,CAAC;YAED,IAAI,MAAM,GAAG,EAAE,CAAA;YACf,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;gBACrB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;oBACpC,CAAC,CAAE,IAAI,CAAC,IAAkB,CAAC,GAAG,CAAC,MAAM,CAAC;oBACtC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;yBACpB,KAAK,CAAC,SAAS,CAAC;yBAChB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;yBACpB,MAAM,CAAC,OAAO,CAAC,CAAA;gBACtB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;oBAClB,OAAO,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,qCAAqC,CAAC,CAAC,CAAA;gBACnF,CAAC;gBACD,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;gBACpC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;oBAC7C,OAAO,YAAY,CACjB,OAAO,CACL,IAAI,EACJ,MAAM,EACN,QAAQ,MAAM,CAAC,KAAK,CAAC,MAAM,SAAS,KAAK,CAAC,MAAM,WAAW,GAAG,wBAAwB;wBACpF,iEAAiE,CACpE,CACF,CAAA;gBACH,CAAC;gBACD,IAAI,MAAM,GAAG,CAAC,CAAA;gBACd,MAAM,KAAK,GAAiB,EAAE,CAAA;gBAC9B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;oBACtB,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;oBACnC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAA;oBACjB,IAAI,CAAC,CAAC,MAAM;wBAAE,MAAM,EAAE,CAAA;;wBACjB,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;gBACzB,CAAC;gBACD,MAAM,GAAG,KAAK,CAAC,MAAM;oBACnB,CAAC,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE;oBACnG,CAAC,CAAC,KAAK,MAAM,kBAAkB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;YAC3J,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,IAAI,CAAgC,CAAA;gBACjE,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;oBACnD,OAAO,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,4BAA4B,CAAC,CAAC,CAAA;gBACnF,CAAC;gBACD,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,MAAsB,EAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAA;gBACrH,IAAI,OAAO,IAAI,CAAC;oBAAE,OAAO,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;gBACrE,MAAM,GAAG,CAAC,CAAC,MAAM,CAAA;gBACjB,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;YAC7F,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAC/B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,oEAAoE;gBACpE,sDAAsD;gBACtD,OAAO,YAAY,CACjB,OAAO,CACL,IAAI,EACJ,SAAS,EACT,GAAG,MAAM,cAAc,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,MAAM,CAAE,GAAa,EAAE,OAAO,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAChH,CACF,CAAA;YACH,CAAC;YACD,IAAI,CAAC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;YACjC,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;YAC9B,OAAO,YAAY,CAAC;gBAClB,EAAE,EAAE,IAAI;gBACR,MAAM,EAAE,GAAG,MAAM,UAAU,IAAI,CAAC,KAAK,CAAC,IAAI,UAAU,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG;gBACjJ,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE;aAC5B,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/dist/tm/perm-ask.d.ts
CHANGED
|
@@ -30,9 +30,49 @@ export declare function setAskBridgeObserver(fn: ((present: boolean) => void) |
|
|
|
30
30
|
/** Extract ctx.ask defensively — host versions before the ctx bridge lack
|
|
31
31
|
* it, and test stubs may not provide it. */
|
|
32
32
|
export declare function askFnOf(ctx: unknown): TmAskFn | null;
|
|
33
|
-
export type AskOutcome = "approved" | "rejected" | "unavailable";
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
|
|
33
|
+
export type AskOutcome = "approved" | "rejected" | "timed-out" | "unavailable";
|
|
34
|
+
/** A remembered allow rule resolves the ask inside the host; a human has to
|
|
35
|
+
* read the dialog, decide and click. 1.5 s separates the two by an order of
|
|
36
|
+
* magnitude on the observed desktop, and this is a HEURISTIC, not a host
|
|
37
|
+
* contract — which is exactly why the sentence it produces says "没有人点这个
|
|
38
|
+
* 窗" only alongside the mechanism, never instead of it. */
|
|
39
|
+
export declare const ASK_AUTO_GRANT_MS = 1500;
|
|
40
|
+
export interface AskResult {
|
|
41
|
+
outcome: AskOutcome;
|
|
42
|
+
/** How long the host took to answer. Undefined when there was no bridge. */
|
|
43
|
+
answeredInMs?: number;
|
|
44
|
+
/** approved AND answered fast enough that nobody can have clicked it — i.e.
|
|
45
|
+
* a saved rule (an "always" the user granted earlier, possibly in another
|
|
46
|
+
* agent's session) answered on their behalf. */
|
|
47
|
+
autoGranted: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** The same drive as `askUserForTarget`, but it reports HOW the approval
|
|
50
|
+
* arrived. Callers that show the user a result need this: an agent that
|
|
51
|
+
* cannot tell "the host remembered an always-allow" from "the user just
|
|
52
|
+
* approved this" will report 免弹窗 as evidence about the allowlist, and that
|
|
53
|
+
* is how a correct observation becomes a wrong conclusion. */
|
|
54
|
+
export declare function askUserForTargetDetailed(ctx: unknown, req: TmAskRequest, waitMs?: number, autoGrantMs?: number): Promise<AskResult>;
|
|
55
|
+
/** The line a caller appends when a governed web fetch/navigation proceeded on
|
|
56
|
+
* an approval rather than on the static allowlist. One function so no tool
|
|
57
|
+
* can half-report it. */
|
|
58
|
+
export declare function askGrantNote(res: AskResult): string;
|
|
59
|
+
/** The unified approval gate auto-rejects an unanswered dialog after
|
|
60
|
+
* `TM_ASK_TIMEOUT_MIN`, but it is only ARMED when R6 is on and the client can
|
|
61
|
+
* reply. Where it is not armed, `await ask()` hung until the user interrupted
|
|
62
|
+
* the whole turn — live evidence: an out-of-allowlist tm_webfetch that showed
|
|
63
|
+
* as a spinning card and then `Tool execution aborted`. A tool-side deadline
|
|
64
|
+
* is the backstop, so no caller has to remember it. */
|
|
65
|
+
export declare const ASK_GRACE_MS = 15000;
|
|
66
|
+
/** Wired once at boot from `resolveAskTimeoutMs() + ASK_GRACE_MS`, so the
|
|
67
|
+
* gate's authoritative reject always wins when it is able to fire and this
|
|
68
|
+
* timer only catches the un-armed case. */
|
|
69
|
+
export declare function setAskWaitMs(ms: number): void;
|
|
70
|
+
export declare function getAskWaitMs(): number;
|
|
71
|
+
/** Drive the official dialog for an out-of-allowlist target. Never throws: a
|
|
72
|
+
* missing bridge is "unavailable", a refusal is "rejected", and a dialog
|
|
73
|
+
* nobody answered is "timed-out" — three different answers that need three
|
|
74
|
+
* different next moves. */
|
|
75
|
+
export declare function askUserForTarget(ctx: unknown, req: TmAskRequest, waitMs?: number): Promise<AskOutcome>;
|
|
76
|
+
export declare function setAskUnavailableNote(note: string): void;
|
|
77
|
+
export declare function askRefusalNote(outcome: AskOutcome, waitMs?: number): string;
|
|
38
78
|
//# sourceMappingURL=perm-ask.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"perm-ask.d.ts","sourceRoot":"","sources":["../../src/tm/perm-ask.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAA;IAClB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAM7D,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAElF;AAED;6CAC6C;AAC7C,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CASpD;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"perm-ask.d.ts","sourceRoot":"","sources":["../../src/tm/perm-ask.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAA;IAClB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAM7D,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAElF;AAED;6CAC6C;AAC7C,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CASpD;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,CAAA;AAE9E;;;;4DAI4D;AAC5D,eAAO,MAAM,iBAAiB,OAAQ,CAAA;AAEtC,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,UAAU,CAAA;IACnB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;qDAEiD;IACjD,WAAW,EAAE,OAAO,CAAA;CACrB;AAED;;;;+DAI+D;AAC/D,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,YAAY,EACjB,MAAM,CAAC,EAAE,MAAM,EACf,WAAW,SAAoB,GAC9B,OAAO,CAAC,SAAS,CAAC,CAkBpB;AAED;;0BAE0B;AAC1B,wBAAgB,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAOnD;AAMD;;;;;wDAKwD;AACxD,eAAO,MAAM,YAAY,QAAS,CAAA;AAIlC;;4CAE4C;AAC5C,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE7C;AACD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;;4BAG4B;AAC5B,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,YAAY,EACjB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,CAAC,CAgBrB;AAUD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAExD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAY,GAAG,MAAM,CAU9E"}
|
package/dist/tm/perm-ask.js
CHANGED
|
@@ -37,19 +37,119 @@ export function askFnOf(ctx) {
|
|
|
37
37
|
}
|
|
38
38
|
return present ? ask : null;
|
|
39
39
|
}
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
|
|
40
|
+
/** A remembered allow rule resolves the ask inside the host; a human has to
|
|
41
|
+
* read the dialog, decide and click. 1.5 s separates the two by an order of
|
|
42
|
+
* magnitude on the observed desktop, and this is a HEURISTIC, not a host
|
|
43
|
+
* contract — which is exactly why the sentence it produces says "没有人点这个
|
|
44
|
+
* 窗" only alongside the mechanism, never instead of it. */
|
|
45
|
+
export const ASK_AUTO_GRANT_MS = 1_500;
|
|
46
|
+
/** The same drive as `askUserForTarget`, but it reports HOW the approval
|
|
47
|
+
* arrived. Callers that show the user a result need this: an agent that
|
|
48
|
+
* cannot tell "the host remembered an always-allow" from "the user just
|
|
49
|
+
* approved this" will report 免弹窗 as evidence about the allowlist, and that
|
|
50
|
+
* is how a correct observation becomes a wrong conclusion. */
|
|
51
|
+
export async function askUserForTargetDetailed(ctx, req, waitMs, autoGrantMs = ASK_AUTO_GRANT_MS) {
|
|
52
|
+
const ask = askFnOf(ctx);
|
|
53
|
+
if (!ask)
|
|
54
|
+
return { outcome: "unavailable", autoGranted: false };
|
|
55
|
+
const budget = Math.max(ASK_WAIT_FLOOR_MS, Math.round(waitMs ?? askWaitMs));
|
|
56
|
+
const t0 = Date.now();
|
|
57
|
+
let timer = null;
|
|
58
|
+
try {
|
|
59
|
+
await new Promise((resolve, reject) => {
|
|
60
|
+
timer = setTimeout(() => reject(TIMED_OUT), budget);
|
|
61
|
+
Promise.resolve(ask(req)).then(resolve, reject);
|
|
62
|
+
});
|
|
63
|
+
const answeredInMs = Date.now() - t0;
|
|
64
|
+
return { outcome: "approved", answeredInMs, autoGranted: answeredInMs < autoGrantMs };
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
return { outcome: err === TIMED_OUT ? "timed-out" : "rejected", answeredInMs: Date.now() - t0, autoGranted: false };
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
if (timer)
|
|
71
|
+
clearTimeout(timer);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** The line a caller appends when a governed web fetch/navigation proceeded on
|
|
75
|
+
* an approval rather than on the static allowlist. One function so no tool
|
|
76
|
+
* can half-report it. */
|
|
77
|
+
export function askGrantNote(res) {
|
|
78
|
+
if (res.outcome !== "approved")
|
|
79
|
+
return "";
|
|
80
|
+
return res.autoGranted
|
|
81
|
+
? `\n(本次不是静态白名单放行:宿主按已记住的规则直接准了,${res.answeredInMs ?? 0}ms 内没有人可能点过这个窗。` +
|
|
82
|
+
`那通常是用户先前点过的"始终允许"——它按项目生效,对所有 agent 会话都算,所以这不证明本会话被单独征求过意见。` +
|
|
83
|
+
`要收回:在宿主的权限设置里删掉那条规则。)`
|
|
84
|
+
: `\n(本次由用户刚刚在确认窗里批准,仅这一会话有效。)`;
|
|
85
|
+
}
|
|
86
|
+
/** Our own reject sentinel — an `ask()` that never settles has to be told
|
|
87
|
+
* apart from a dialog the user actually answered "no". */
|
|
88
|
+
const TIMED_OUT = Symbol("ask-timed-out");
|
|
89
|
+
/** The unified approval gate auto-rejects an unanswered dialog after
|
|
90
|
+
* `TM_ASK_TIMEOUT_MIN`, but it is only ARMED when R6 is on and the client can
|
|
91
|
+
* reply. Where it is not armed, `await ask()` hung until the user interrupted
|
|
92
|
+
* the whole turn — live evidence: an out-of-allowlist tm_webfetch that showed
|
|
93
|
+
* as a spinning card and then `Tool execution aborted`. A tool-side deadline
|
|
94
|
+
* is the backstop, so no caller has to remember it. */
|
|
95
|
+
export const ASK_GRACE_MS = 15_000;
|
|
96
|
+
const ASK_WAIT_FLOOR_MS = 5_000;
|
|
97
|
+
let askWaitMs = 75_000;
|
|
98
|
+
/** Wired once at boot from `resolveAskTimeoutMs() + ASK_GRACE_MS`, so the
|
|
99
|
+
* gate's authoritative reject always wins when it is able to fire and this
|
|
100
|
+
* timer only catches the un-armed case. */
|
|
101
|
+
export function setAskWaitMs(ms) {
|
|
102
|
+
if (Number.isFinite(ms) && ms >= ASK_WAIT_FLOOR_MS)
|
|
103
|
+
askWaitMs = Math.round(ms);
|
|
104
|
+
}
|
|
105
|
+
export function getAskWaitMs() {
|
|
106
|
+
return askWaitMs;
|
|
107
|
+
}
|
|
108
|
+
/** Drive the official dialog for an out-of-allowlist target. Never throws: a
|
|
109
|
+
* missing bridge is "unavailable", a refusal is "rejected", and a dialog
|
|
110
|
+
* nobody answered is "timed-out" — three different answers that need three
|
|
111
|
+
* different next moves. */
|
|
112
|
+
export async function askUserForTarget(ctx, req, waitMs) {
|
|
44
113
|
const ask = askFnOf(ctx);
|
|
45
114
|
if (!ask)
|
|
46
115
|
return "unavailable";
|
|
116
|
+
const budget = Math.max(ASK_WAIT_FLOOR_MS, Math.round(waitMs ?? askWaitMs));
|
|
117
|
+
let timer = null;
|
|
47
118
|
try {
|
|
48
|
-
await
|
|
119
|
+
await new Promise((resolve, reject) => {
|
|
120
|
+
timer = setTimeout(() => reject(TIMED_OUT), budget);
|
|
121
|
+
Promise.resolve(ask(req)).then(resolve, reject);
|
|
122
|
+
});
|
|
49
123
|
return "approved";
|
|
50
124
|
}
|
|
51
|
-
catch {
|
|
52
|
-
return "rejected";
|
|
125
|
+
catch (err) {
|
|
126
|
+
return err === TIMED_OUT ? "timed-out" : "rejected";
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
if (timer)
|
|
130
|
+
clearTimeout(timer);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** One sentence naming WHY the dialog did not grant access. This is not
|
|
134
|
+
* cosmetic: "用户未批准" for a dialog nobody saw teaches the agent to give up
|
|
135
|
+
* (or retry silently) instead of telling the human to look at the screen. */
|
|
136
|
+
/** What "we could not ask" means on THIS host generation. v1 says 旧版协议
|
|
137
|
+
* because there it is true; the v2 personality replaces the sentence because
|
|
138
|
+
* on v2 the absence is the NEW protocol, and telling the model the host is old
|
|
139
|
+
* would send it looking for an upgrade it already has. */
|
|
140
|
+
let unavailableNote = "宿主无法弹出确认窗口(旧版协议)。";
|
|
141
|
+
export function setAskUnavailableNote(note) {
|
|
142
|
+
if (typeof note === "string" && note.trim())
|
|
143
|
+
unavailableNote = note;
|
|
144
|
+
}
|
|
145
|
+
export function askRefusalNote(outcome, waitMs = askWaitMs) {
|
|
146
|
+
if (outcome === "rejected")
|
|
147
|
+
return "用户未批准。";
|
|
148
|
+
if (outcome === "timed-out") {
|
|
149
|
+
return (`确认窗无人应答——我等满了 ${Math.round(waitMs / 1000)} 秒(本机 ask 的等待上限)才放弃,` +
|
|
150
|
+
`所以这不是被拒绝,是没有人在界面上点它。请提醒用户查看待确认的对话框;` +
|
|
151
|
+
`如果确实不想开权限,就改用白名单内的源,不要重复调用。`);
|
|
53
152
|
}
|
|
153
|
+
return unavailableNote;
|
|
54
154
|
}
|
|
55
155
|
//# sourceMappingURL=perm-ask.js.map
|
package/dist/tm/perm-ask.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"perm-ask.js","sourceRoot":"","sources":["../../src/tm/perm-ask.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAaH;;6EAE6E;AAC7E,IAAI,iBAAiB,GAAwC,IAAI,CAAA;AACjE,MAAM,UAAU,oBAAoB,CAAC,EAAuC;IAC1E,iBAAiB,GAAG,EAAE,CAAA;AACxB,CAAC;AAED;6CAC6C;AAC7C,MAAM,UAAU,OAAO,CAAC,GAAY;IAClC,MAAM,GAAG,GAAI,GAA4C,EAAE,GAAG,CAAA;IAC9D,MAAM,OAAO,GAAG,OAAO,GAAG,KAAK,UAAU,CAAA;IACzC,IAAI,CAAC;QACH,iBAAiB,EAAE,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAE,GAAe,CAAC,CAAC,CAAC,IAAI,CAAA;AAC1C,CAAC;AAID;;
|
|
1
|
+
{"version":3,"file":"perm-ask.js","sourceRoot":"","sources":["../../src/tm/perm-ask.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipelines.d.ts","sourceRoot":"","sources":["../../src/tm/pipelines.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,KAAK,MAAM,MAAM,aAAa,CAAA;AAG1C,OAAO,EAAgE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACpH,OAAO,EAA0C,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAIL,iBAAiB,EAClB,MAAM,cAAc,CAAA;AAmBrB,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,OAAO,CAAA;IACf,CAAC,EAAE,OAAO,CAAA;IACV,GAAG,EAAE,QAAQ,CAAA;IACb,KAAK,EAAE,QAAQ,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC,UAAU,CAAA;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,cAAc,CAAA;IACpB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAID,UAAU,aAAa;IACrB,WAAW,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA;IACjD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AA6CD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA6ClE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM;;qBA0BjB,MAAM,QAAQ,MAAM,WAAW,MAAM,QAAQ,aAAa,KAAG,OAAO;sBA0D7D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,OAAO,KAAG,OAAO,CAAC,OAAO,CAAC;sBAgDxD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,OAAO,KAAG,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"pipelines.d.ts","sourceRoot":"","sources":["../../src/tm/pipelines.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,KAAK,MAAM,MAAM,aAAa,CAAA;AAG1C,OAAO,EAAgE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACpH,OAAO,EAA0C,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAIL,iBAAiB,EAClB,MAAM,cAAc,CAAA;AAmBrB,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,OAAO,CAAA;IACf,CAAC,EAAE,OAAO,CAAA;IACV,GAAG,EAAE,QAAQ,CAAA;IACb,KAAK,EAAE,QAAQ,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC,UAAU,CAAA;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,cAAc,CAAA;IACpB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAID,UAAU,aAAa;IACrB,WAAW,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA;IACjD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AA6CD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA6ClE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM;;qBA0BjB,MAAM,QAAQ,MAAM,WAAW,MAAM,QAAQ,aAAa,KAAG,OAAO;sBA0D7D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,OAAO,KAAG,OAAO,CAAC,OAAO,CAAC;sBAgDxD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,OAAO,KAAG,OAAO,CAAC,OAAO,CAAC;sBAkExD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,OAAO,KAAG,OAAO,CAAC,OAAO,CAAC;uBA8CvD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,OAAO,KAAG,OAAO,CAAC,OAAO,CAAC;;EAiH1F;AAED,kFAAkF;AAClF,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAA"}
|
package/dist/tm/pipelines.js
CHANGED
|
@@ -310,6 +310,21 @@ export function buildPipelines(deps) {
|
|
|
310
310
|
return tmError(tool, "client", unwrapped.message);
|
|
311
311
|
const content = extractText(unwrapped.data);
|
|
312
312
|
const matchLines = content ? content.split(/\r?\n/).filter((l) => l.trim()).length : 0;
|
|
313
|
+
if (!matchLines) {
|
|
314
|
+
// A bare empty result reads as "the search is broken", and the agent
|
|
315
|
+
// then re-runs it a wider way or concludes the opposite of the truth
|
|
316
|
+
// (live: a 0-hit search the model called a tool failure). 0 is an
|
|
317
|
+
// ANSWER, so print it as one — with the scope it applies to.
|
|
318
|
+
// …and a widening hint ONLY when the caller narrowed it — telling a
|
|
319
|
+
// root-wide search to drop `path` is noise that costs a round.
|
|
320
|
+
const scopeNote = requested
|
|
321
|
+
? `\n本次范围由 path="${shorten(requested, 80)}" 限定——要搜整个工作区就去掉 path 再试一次。`
|
|
322
|
+
: "";
|
|
323
|
+
return govern(stepId, tool, `(0 命中)pattern="${pattern}" · 范围=${shorten(scopeDir, 120)}${scopeNote}\n这只表示"在该目录及其子目录里没有匹配",不证明整个仓库没有:这是正则,换写法常能救回来([Aa]rk 同时命中 Ark/ark),或直接 tm_read 读你怀疑的那个文件。`, {
|
|
324
|
+
contentType: "text",
|
|
325
|
+
clue: `pattern=${shorten(pattern, 60)}, 命中 0 行, dir=${shorten(scopeDir, 80)}`,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
313
328
|
return govern(stepId, tool, content, {
|
|
314
329
|
contentType: detectContentType(content),
|
|
315
330
|
clue: `pattern=${shorten(pattern, 60)}, 命中 ${matchLines} 行, dir=${shorten(scopeDir, 80)}`,
|
|
@@ -345,8 +360,15 @@ export function buildPipelines(deps) {
|
|
|
345
360
|
// workspace all session.
|
|
346
361
|
const cwd = ctxDir(ctx);
|
|
347
362
|
const output = await runShellCommand(deps.$, command, cwd);
|
|
348
|
-
|
|
349
|
-
|
|
363
|
+
// Empty stdout used to come back as an empty result — indistinguishable
|
|
364
|
+
// from "the tool never ran", so the model re-issued the command or
|
|
365
|
+
// concluded the tool was broken (live: a `-Skip 168` on a file PowerShell
|
|
366
|
+
// decodes as 168 lines, where the honest answer is "0 行输出").
|
|
367
|
+
const text = output.trim() === ""
|
|
368
|
+
? `(命令执行完成,stdout 为空 = 0 行输出 · cwd=${shorten(cwd, 120)})\n空输出是命令给出的答案,不是工具失败。若你预期有内容:先核对相对路径是按上面的 cwd 解析的;读文件内容请用 tm_read——Windows PowerShell 5.1 的默认解码口径与 tm_read/tm_grep 不一致,含中文的 UTF-8 文件在它眼里行数可能更少,按行取片段时这会直接骗到你。`
|
|
369
|
+
: output;
|
|
370
|
+
return govern(stepId, tool, text, {
|
|
371
|
+
contentType: detectContentType(text),
|
|
350
372
|
clue: `cmd=${shorten(command, 60)}, dir=${shorten(cwd, 80)}`,
|
|
351
373
|
});
|
|
352
374
|
}
|