openclaw-lark-multi-agent 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/README.zh-CN.md +10 -0
- package/dist/feishu-bot.d.ts +16 -0
- package/dist/feishu-bot.js +303 -32
- package/dist/go-controller.d.ts +86 -12
- package/dist/go-controller.js +435 -131
- package/dist/go-execution.d.ts +10 -0
- package/dist/go-execution.js +1 -0
- package/dist/go-runtime-gate.d.ts +32 -0
- package/dist/go-runtime-gate.js +56 -0
- package/dist/message-store.d.ts +83 -0
- package/dist/message-store.js +142 -2
- package/dist/openclaw-client.d.ts +114 -0
- package/dist/openclaw-client.js +166 -12
- package/dist/provider-errors.d.ts +24 -0
- package/dist/provider-errors.js +344 -0
- package/dist/terminal-errors.d.ts +3 -0
- package/dist/terminal-errors.js +2 -1
- package/package.json +8 -3
- package/plugins/lma-go/README.md +22 -0
- package/plugins/lma-go/dist/index.js +35 -0
- package/plugins/lma-go/dist/policy.js +25 -0
- package/plugins/lma-go/dist/registry.js +37 -0
- package/plugins/lma-go/openclaw.plugin.json +1 -0
- package/plugins/lma-go/package.json +1 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** An authoritative terminal result of one generation-pinned, admitted executor run.
|
|
2
|
+
* Admission, terminal result and platform delivery receipts are separate facts. */
|
|
3
|
+
export type GoExecutorResult = {
|
|
4
|
+
sessionKey: string;
|
|
5
|
+
sessionId: string;
|
|
6
|
+
runId: string;
|
|
7
|
+
disposition: "visible" | "silent" | "empty" | "error";
|
|
8
|
+
text: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Pure adapter contract. Independent queries are observations, not a global atomic snapshot. */
|
|
2
|
+
export type GoRuntimeSnapshot = {
|
|
3
|
+
sessionKey: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
goalId: string;
|
|
6
|
+
goalRevision: number;
|
|
7
|
+
executorRunIds: string[];
|
|
8
|
+
checkerRunId: string;
|
|
9
|
+
complete: boolean;
|
|
10
|
+
archived: boolean;
|
|
11
|
+
independent?: boolean;
|
|
12
|
+
queryAvailable?: boolean;
|
|
13
|
+
running?: boolean;
|
|
14
|
+
atomicSnapshot?: boolean;
|
|
15
|
+
queuedInputs: number;
|
|
16
|
+
recoverableInputs: number;
|
|
17
|
+
pendingDeliveries: number;
|
|
18
|
+
tasks: Array<{
|
|
19
|
+
taskId: string;
|
|
20
|
+
runId: string;
|
|
21
|
+
parentTaskId: string | null;
|
|
22
|
+
state: string;
|
|
23
|
+
deliveryState: string;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
26
|
+
export declare function goSnapshotBlocked(s: GoRuntimeSnapshot, expected: {
|
|
27
|
+
sessionKey: string;
|
|
28
|
+
sessionId: string;
|
|
29
|
+
goalId: string;
|
|
30
|
+
goalRevision: number;
|
|
31
|
+
executorRunIds: string[];
|
|
32
|
+
}): string | null;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function goSnapshotBlocked(s, expected) {
|
|
2
|
+
if (!s.independent && (!s.checkerRunId || !expected.executorRunIds.length || expected.executorRunIds.some(id => !id)))
|
|
3
|
+
return "missing run identity";
|
|
4
|
+
if (s.independent ? !s.queryAvailable : !s.complete)
|
|
5
|
+
return "incomplete snapshot";
|
|
6
|
+
if (s.independent && s.queuedInputs !== 0)
|
|
7
|
+
return "pending or unknown input/delivery";
|
|
8
|
+
if (s.independent && s.running !== false)
|
|
9
|
+
return "active or unknown session";
|
|
10
|
+
if (s.archived || s.sessionKey !== expected.sessionKey || s.sessionId !== expected.sessionId || (!s.independent && (s.goalId !== expected.goalId || s.goalRevision !== expected.goalRevision)))
|
|
11
|
+
return "identity/version changed";
|
|
12
|
+
if (!s.independent && [s.queuedInputs, s.recoverableInputs, s.pendingDeliveries].some(n => !Number.isSafeInteger(n) || n !== 0))
|
|
13
|
+
return "pending or unknown input/delivery";
|
|
14
|
+
if (!s.independent && (new Set(s.executorRunIds).size !== s.executorRunIds.length || s.executorRunIds.length !== expected.executorRunIds.length || expected.executorRunIds.some(id => !s.executorRunIds.includes(id))))
|
|
15
|
+
return "run association changed";
|
|
16
|
+
const byId = new Map(s.tasks.map(t => [t.taskId, t]));
|
|
17
|
+
if (byId.size !== s.tasks.length)
|
|
18
|
+
return "duplicate task identity";
|
|
19
|
+
for (const task of s.tasks) {
|
|
20
|
+
const visited = new Set();
|
|
21
|
+
let cursor = task;
|
|
22
|
+
while (cursor) {
|
|
23
|
+
if (visited.has(cursor.taskId))
|
|
24
|
+
return "cyclic task ancestry";
|
|
25
|
+
visited.add(cursor.taskId);
|
|
26
|
+
cursor = cursor.parentTaskId ? byId.get(cursor.parentTaskId) : undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const related = new Set(s.tasks.filter(t => expected.executorRunIds.includes(t.runId)).map(t => t.taskId));
|
|
30
|
+
// Fixed point, not ordering/prefix inference. Exclude no task just because its text says checker.
|
|
31
|
+
let changed = true;
|
|
32
|
+
while (changed) {
|
|
33
|
+
changed = false;
|
|
34
|
+
for (const t of s.tasks)
|
|
35
|
+
if (t.parentTaskId && related.has(t.parentTaskId) && !related.has(t.taskId)) {
|
|
36
|
+
related.add(t.taskId);
|
|
37
|
+
changed = true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const t of s.tasks) {
|
|
41
|
+
if (t.parentTaskId && !byId.has(t.parentTaskId))
|
|
42
|
+
return "missing ancestor";
|
|
43
|
+
if (!related.has(t.taskId) && !s.independent)
|
|
44
|
+
continue;
|
|
45
|
+
if (t.runId === s.checkerRunId)
|
|
46
|
+
return "checker/executor association conflict";
|
|
47
|
+
if (!["completed", "failed", "cancelled"].includes(t.state))
|
|
48
|
+
return "active or unknown task";
|
|
49
|
+
if (!["delivered", "not_required"].includes(t.deliveryState))
|
|
50
|
+
return "pending or unknown task return";
|
|
51
|
+
}
|
|
52
|
+
// Each root run needs explicit terminal state evidence; absence is never idle.
|
|
53
|
+
if (!s.independent && expected.executorRunIds.some(id => !s.tasks.some(t => t.runId === id)))
|
|
54
|
+
return "missing root run";
|
|
55
|
+
return null;
|
|
56
|
+
}
|
package/dist/message-store.d.ts
CHANGED
|
@@ -103,6 +103,85 @@ export declare class MessageStore {
|
|
|
103
103
|
constructor(dbPath?: string);
|
|
104
104
|
/** Go / Go-Checker supervision state (design: artifacts/lma-go-checker-design-20260917 §10). */
|
|
105
105
|
private initGoTables;
|
|
106
|
+
private ensureGoIntegrationSchema;
|
|
107
|
+
bindGoNativeIdentity(goId: number, identity: {
|
|
108
|
+
nativeGoalId?: string;
|
|
109
|
+
executorSessionId: string;
|
|
110
|
+
checkerSessionId: string;
|
|
111
|
+
checkerRunId?: string;
|
|
112
|
+
executorRunIds: string[];
|
|
113
|
+
goalUpdatedAt?: number;
|
|
114
|
+
goalRevision?: number | null;
|
|
115
|
+
}): void;
|
|
116
|
+
getGoNativeIdentity(goId: number): any;
|
|
117
|
+
setGoCriteria(goId: number, criteria: Array<{
|
|
118
|
+
id: string;
|
|
119
|
+
text: string;
|
|
120
|
+
}>): void;
|
|
121
|
+
getGoCriteria(goId: number): Array<{
|
|
122
|
+
id: string;
|
|
123
|
+
text: string;
|
|
124
|
+
}>;
|
|
125
|
+
getGoDeliveryOperation(goId: number, key: string): any;
|
|
126
|
+
recordGoDeliveryOperation(goId: number, key: string, state: "attempted" | "accepted" | "unknown" | "rejected", response?: unknown): void;
|
|
127
|
+
/** Durable initial-execution intent, separate from the at-most-once send ledger. */
|
|
128
|
+
createGoBootstrap(goId: number): void;
|
|
129
|
+
getGoBootstrap(goId: number): string | null;
|
|
130
|
+
finishGoBootstrap(goId: number, state: "done" | "superseded"): void;
|
|
131
|
+
isGoActionComplete(goId: number, actionKey: string): boolean;
|
|
132
|
+
listGoSends(goId: number): Array<{
|
|
133
|
+
actionKey: string;
|
|
134
|
+
idempotencyKey: string;
|
|
135
|
+
sessionKey: string;
|
|
136
|
+
sessionId: string;
|
|
137
|
+
}>;
|
|
138
|
+
recordGoRunResult(goId: number, actionKey: string, result: {
|
|
139
|
+
state: "complete" | "unknown";
|
|
140
|
+
text?: string;
|
|
141
|
+
runId: string;
|
|
142
|
+
executorResult?: import("./go-execution.js").GoExecutorResult;
|
|
143
|
+
}): void;
|
|
144
|
+
getGoRunResult(goId: number, actionKey: string): any;
|
|
145
|
+
listUnknownGoRunResults(goId: number): Array<{
|
|
146
|
+
actionKey: string;
|
|
147
|
+
runId: string;
|
|
148
|
+
sessionKey: string;
|
|
149
|
+
sessionId: string;
|
|
150
|
+
}>;
|
|
151
|
+
hasUnknownGoRunResults(goId: number): boolean;
|
|
152
|
+
associateGoAdmittedRun(goId: number, runId: string, executor: boolean): void;
|
|
153
|
+
/** Durable delivery ownership includes completed/stopped jobs, not only active ones. */
|
|
154
|
+
ownsGoExecutorRun(botName: string, chatId: string, sessionKey: string, sessionId: string, runId: string): boolean;
|
|
155
|
+
recordGoVisibleEvidence(goId: number, sourceKey: string, payload: {
|
|
156
|
+
sessionId: string;
|
|
157
|
+
runId: string;
|
|
158
|
+
kind: string;
|
|
159
|
+
text: string;
|
|
160
|
+
attachments: unknown[];
|
|
161
|
+
}): boolean;
|
|
162
|
+
recordGoDeliveryReceipt(goId: number, sourceKey: string, receipt: unknown): void;
|
|
163
|
+
listGoVisibleEvidence(goId: number): any[];
|
|
164
|
+
bindGoSendIdentity(goId: number, actionKey: string, identity: {
|
|
165
|
+
sessionKey: string;
|
|
166
|
+
sessionId: string;
|
|
167
|
+
}): void;
|
|
168
|
+
listUnresolvedGoSends(goId: number): Array<{
|
|
169
|
+
actionKey: string;
|
|
170
|
+
idempotencyKey: string;
|
|
171
|
+
sessionKey: string;
|
|
172
|
+
sessionId: string;
|
|
173
|
+
}>;
|
|
174
|
+
recordGoActionAttempt(goId: number, actionKey: string): {
|
|
175
|
+
state: string;
|
|
176
|
+
idempotency_key: string;
|
|
177
|
+
receipt_json: string;
|
|
178
|
+
} | undefined;
|
|
179
|
+
getGoActionLifecycle(goId: number, actionKey: string): {
|
|
180
|
+
state: string;
|
|
181
|
+
idempotency_key: string;
|
|
182
|
+
receipt_json: string;
|
|
183
|
+
} | undefined;
|
|
184
|
+
recordGoActionOutcome(goId: number, actionKey: string, state: 'accepted' | 'unknown', receipt?: unknown): void;
|
|
106
185
|
observeRunError(botName: string, chatId: string, provenance: ErrorProvenance): PendingErrorNotice;
|
|
107
186
|
getErrorNotice(botName: string, chatId: string, noticeId: string): PendingErrorNotice | undefined;
|
|
108
187
|
saveErrorNotice(record: PendingErrorNotice): void;
|
|
@@ -245,6 +324,7 @@ export declare class MessageStore {
|
|
|
245
324
|
executorSessionId: string;
|
|
246
325
|
checkerSessionKey: string;
|
|
247
326
|
checkerModel: string;
|
|
327
|
+
state?: "STARTING" | "WAITING_WORK";
|
|
248
328
|
}): number;
|
|
249
329
|
private rowToGoJob;
|
|
250
330
|
getActiveGoJob(botName: string, chatId: string): GoJobRow | null;
|
|
@@ -266,8 +346,11 @@ export declare class MessageStore {
|
|
|
266
346
|
finishGoCheck(goId: number, checkId: string, state: string, action: string, summary: string, raw: string): void;
|
|
267
347
|
/** Idempotent action claim: returns true when newly claimed (caller performs the action), false when it already existed. */
|
|
268
348
|
claimGoAction(goId: number, actionKey: string, kind: string, payload: string): boolean;
|
|
349
|
+
hasUnresolvedGoActions(goId: number): boolean;
|
|
269
350
|
completeGoAction(goId: number, actionKey: string, receipt: string): void;
|
|
270
351
|
setGoModelPref(botName: string, chatId: string, model: string | null): void;
|
|
271
352
|
getGoModelPref(botName: string, chatId: string): string | null;
|
|
353
|
+
private closeObservers;
|
|
354
|
+
onClose(callback: () => void): () => boolean;
|
|
272
355
|
close(): void;
|
|
273
356
|
}
|
package/dist/message-store.js
CHANGED
|
@@ -83,6 +83,136 @@ export class MessageStore {
|
|
|
83
83
|
);
|
|
84
84
|
`);
|
|
85
85
|
}
|
|
86
|
+
ensureGoIntegrationSchema() {
|
|
87
|
+
this.db.exec(`CREATE TABLE IF NOT EXISTS go_native_bindings (
|
|
88
|
+
go_id INTEGER PRIMARY KEY, identity_json TEXT NOT NULL);
|
|
89
|
+
CREATE TABLE IF NOT EXISTS go_action_lifecycle (
|
|
90
|
+
go_id INTEGER NOT NULL, action_key TEXT NOT NULL, state TEXT NOT NULL,
|
|
91
|
+
idempotency_key TEXT NOT NULL, receipt_json TEXT NOT NULL DEFAULT '',
|
|
92
|
+
PRIMARY KEY(go_id, action_key));
|
|
93
|
+
CREATE TABLE IF NOT EXISTS go_visible_evidence (
|
|
94
|
+
go_id INTEGER NOT NULL, source_key TEXT NOT NULL, payload_json TEXT NOT NULL,
|
|
95
|
+
receipt_json TEXT NOT NULL DEFAULT '', PRIMARY KEY(go_id, source_key));`);
|
|
96
|
+
}
|
|
97
|
+
bindGoNativeIdentity(goId, identity) {
|
|
98
|
+
this.ensureGoIntegrationSchema();
|
|
99
|
+
this.db.prepare('INSERT INTO go_native_bindings VALUES (?, ?)').run(goId, JSON.stringify(identity));
|
|
100
|
+
}
|
|
101
|
+
getGoNativeIdentity(goId) {
|
|
102
|
+
this.ensureGoIntegrationSchema();
|
|
103
|
+
const row = this.db.prepare('SELECT identity_json FROM go_native_bindings WHERE go_id=?').get(goId);
|
|
104
|
+
return row ? JSON.parse(row.identity_json) : null;
|
|
105
|
+
}
|
|
106
|
+
setGoCriteria(goId, criteria) {
|
|
107
|
+
this.db.exec("CREATE TABLE IF NOT EXISTS go_criteria(go_id INTEGER PRIMARY KEY,criteria_json TEXT NOT NULL)");
|
|
108
|
+
this.db.prepare("INSERT OR REPLACE INTO go_criteria VALUES (?,?)").run(goId, JSON.stringify(criteria));
|
|
109
|
+
}
|
|
110
|
+
getGoCriteria(goId) {
|
|
111
|
+
this.db.exec("CREATE TABLE IF NOT EXISTS go_criteria(go_id INTEGER PRIMARY KEY,criteria_json TEXT NOT NULL)");
|
|
112
|
+
const row = this.db.prepare("SELECT criteria_json FROM go_criteria WHERE go_id=?").get(goId);
|
|
113
|
+
return row ? JSON.parse(row.criteria_json) : [];
|
|
114
|
+
}
|
|
115
|
+
getGoDeliveryOperation(goId, key) {
|
|
116
|
+
this.db.exec("CREATE TABLE IF NOT EXISTS go_delivery_operations(go_id INTEGER,operation_key TEXT,state TEXT,response_json TEXT,PRIMARY KEY(go_id,operation_key))");
|
|
117
|
+
return this.db.prepare("SELECT * FROM go_delivery_operations WHERE go_id=? AND operation_key=?").get(goId, key);
|
|
118
|
+
}
|
|
119
|
+
recordGoDeliveryOperation(goId, key, state, response) {
|
|
120
|
+
this.getGoDeliveryOperation(goId, key);
|
|
121
|
+
this.db.prepare("INSERT INTO go_delivery_operations VALUES (?,?,?,?) ON CONFLICT(go_id,operation_key) DO UPDATE SET state=excluded.state,response_json=excluded.response_json WHERE go_delivery_operations.state!='accepted'").run(goId, key, state, response === undefined ? "" : JSON.stringify(response));
|
|
122
|
+
}
|
|
123
|
+
/** Durable initial-execution intent, separate from the at-most-once send ledger. */
|
|
124
|
+
createGoBootstrap(goId) {
|
|
125
|
+
this.db.exec("CREATE TABLE IF NOT EXISTS go_bootstrap(go_id INTEGER PRIMARY KEY,state TEXT NOT NULL)");
|
|
126
|
+
this.db.prepare("INSERT OR IGNORE INTO go_bootstrap VALUES (?,'pending')").run(goId);
|
|
127
|
+
}
|
|
128
|
+
getGoBootstrap(goId) {
|
|
129
|
+
this.db.exec("CREATE TABLE IF NOT EXISTS go_bootstrap(go_id INTEGER PRIMARY KEY,state TEXT NOT NULL)");
|
|
130
|
+
return this.db.prepare("SELECT state FROM go_bootstrap WHERE go_id=?").get(goId)?.state ?? null;
|
|
131
|
+
}
|
|
132
|
+
finishGoBootstrap(goId, state) {
|
|
133
|
+
this.getGoBootstrap(goId);
|
|
134
|
+
this.db.prepare("UPDATE go_bootstrap SET state=? WHERE go_id=?").run(state, goId);
|
|
135
|
+
}
|
|
136
|
+
isGoActionComplete(goId, actionKey) {
|
|
137
|
+
return this.db.prepare("SELECT done FROM go_actions WHERE go_id=? AND action_key=?").get(goId, actionKey)?.done === 1;
|
|
138
|
+
}
|
|
139
|
+
listGoSends(goId) {
|
|
140
|
+
this.listUnresolvedGoSends(goId);
|
|
141
|
+
return this.db.prepare("SELECT s.*,l.idempotency_key FROM go_send_identities s JOIN go_action_lifecycle l ON s.go_id=l.go_id AND s.action_key=l.action_key WHERE s.go_id=?").all(goId)
|
|
142
|
+
.map(r => ({ actionKey: r.action_key, idempotencyKey: r.idempotency_key, ...JSON.parse(r.identity_json) }));
|
|
143
|
+
}
|
|
144
|
+
recordGoRunResult(goId, actionKey, result) {
|
|
145
|
+
this.db.exec("CREATE TABLE IF NOT EXISTS go_run_results(go_id INTEGER,action_key TEXT,result_json TEXT,PRIMARY KEY(go_id,action_key))");
|
|
146
|
+
this.db.prepare("INSERT OR REPLACE INTO go_run_results VALUES (?,?,?)").run(goId, actionKey, JSON.stringify(result));
|
|
147
|
+
}
|
|
148
|
+
getGoRunResult(goId, actionKey) {
|
|
149
|
+
this.db.exec("CREATE TABLE IF NOT EXISTS go_run_results(go_id INTEGER,action_key TEXT,result_json TEXT,PRIMARY KEY(go_id,action_key))");
|
|
150
|
+
const row = this.db.prepare("SELECT result_json FROM go_run_results WHERE go_id=? AND action_key=?").get(goId, actionKey);
|
|
151
|
+
return row ? JSON.parse(row.result_json) : null;
|
|
152
|
+
}
|
|
153
|
+
listUnknownGoRunResults(goId) {
|
|
154
|
+
this.getGoRunResult(goId, "");
|
|
155
|
+
this.listUnresolvedGoSends(goId);
|
|
156
|
+
return this.db.prepare("SELECT r.action_key,r.result_json,s.identity_json FROM go_run_results r JOIN go_send_identities s ON r.go_id=s.go_id AND r.action_key=s.action_key WHERE r.go_id=?").all(goId).flatMap(r => {
|
|
157
|
+
const result = JSON.parse(r.result_json);
|
|
158
|
+
return result.state === "unknown" ? [{ actionKey: r.action_key, runId: result.runId, ...JSON.parse(r.identity_json) }] : [];
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
hasUnknownGoRunResults(goId) {
|
|
162
|
+
this.getGoRunResult(goId, "");
|
|
163
|
+
return this.db.prepare("SELECT result_json FROM go_run_results WHERE go_id=?").all(goId).some(r => JSON.parse(r.result_json).state === "unknown");
|
|
164
|
+
}
|
|
165
|
+
associateGoAdmittedRun(goId, runId, executor) {
|
|
166
|
+
const identity = this.getGoNativeIdentity(goId);
|
|
167
|
+
if (!identity)
|
|
168
|
+
throw new Error("Missing independent identity");
|
|
169
|
+
if (executor)
|
|
170
|
+
identity.executorRunIds = [...new Set([...identity.executorRunIds, runId])];
|
|
171
|
+
else
|
|
172
|
+
identity.checkerRunId = runId;
|
|
173
|
+
this.db.prepare("UPDATE go_native_bindings SET identity_json=? WHERE go_id=?").run(JSON.stringify(identity), goId);
|
|
174
|
+
}
|
|
175
|
+
/** Durable delivery ownership includes completed/stopped jobs, not only active ones. */
|
|
176
|
+
ownsGoExecutorRun(botName, chatId, sessionKey, sessionId, runId) {
|
|
177
|
+
this.ensureGoIntegrationSchema();
|
|
178
|
+
return this.db.prepare("SELECT j.executor_session_key,j.executor_session_id,b.identity_json FROM go_jobs j JOIN go_native_bindings b ON j.id=b.go_id WHERE j.bot_name=? AND j.chat_id=? AND j.executor_session_id=?").all(botName, chatId, sessionId)
|
|
179
|
+
.some(row => [row.executor_session_key, `agent:main:${row.executor_session_key}`].includes(sessionKey) && JSON.parse(row.identity_json).executorRunIds.includes(runId));
|
|
180
|
+
}
|
|
181
|
+
recordGoVisibleEvidence(goId, sourceKey, payload) {
|
|
182
|
+
this.ensureGoIntegrationSchema();
|
|
183
|
+
return this.db.prepare('INSERT OR IGNORE INTO go_visible_evidence (go_id,source_key,payload_json) VALUES (?,?,?)').run(goId, sourceKey, JSON.stringify(payload)).changes === 1;
|
|
184
|
+
}
|
|
185
|
+
recordGoDeliveryReceipt(goId, sourceKey, receipt) {
|
|
186
|
+
this.ensureGoIntegrationSchema();
|
|
187
|
+
this.db.prepare('UPDATE go_visible_evidence SET receipt_json=? WHERE go_id=? AND source_key=?').run(JSON.stringify(receipt), goId, sourceKey);
|
|
188
|
+
}
|
|
189
|
+
listGoVisibleEvidence(goId) {
|
|
190
|
+
this.ensureGoIntegrationSchema();
|
|
191
|
+
return this.db.prepare('SELECT * FROM go_visible_evidence WHERE go_id=? ORDER BY rowid').all(goId)
|
|
192
|
+
.map(r => ({ sourceKey: r.source_key, ...JSON.parse(r.payload_json), receipt: r.receipt_json ? JSON.parse(r.receipt_json) : null }));
|
|
193
|
+
}
|
|
194
|
+
bindGoSendIdentity(goId, actionKey, identity) {
|
|
195
|
+
this.db.exec("CREATE TABLE IF NOT EXISTS go_send_identities(go_id INTEGER,action_key TEXT,identity_json TEXT,PRIMARY KEY(go_id,action_key))");
|
|
196
|
+
this.db.prepare("INSERT OR IGNORE INTO go_send_identities VALUES (?,?,?)").run(goId, actionKey, JSON.stringify(identity));
|
|
197
|
+
}
|
|
198
|
+
listUnresolvedGoSends(goId) {
|
|
199
|
+
this.db.exec("CREATE TABLE IF NOT EXISTS go_send_identities(go_id INTEGER,action_key TEXT,identity_json TEXT,PRIMARY KEY(go_id,action_key))");
|
|
200
|
+
return this.db.prepare("SELECT s.*,l.idempotency_key FROM go_send_identities s JOIN go_action_lifecycle l ON s.go_id=l.go_id AND s.action_key=l.action_key WHERE s.go_id=? AND l.state!='accepted'").all(goId).map(r => ({ actionKey: r.action_key, idempotencyKey: r.idempotency_key, ...JSON.parse(r.identity_json) }));
|
|
201
|
+
}
|
|
202
|
+
recordGoActionAttempt(goId, actionKey) {
|
|
203
|
+
this.ensureGoIntegrationSchema();
|
|
204
|
+
const idempotencyKey = `lma-go:${goId}:${actionKey}`;
|
|
205
|
+
this.db.prepare("INSERT OR IGNORE INTO go_action_lifecycle (go_id,action_key,state,idempotency_key) VALUES (?,?,'attempted',?)").run(goId, actionKey, idempotencyKey);
|
|
206
|
+
return this.getGoActionLifecycle(goId, actionKey);
|
|
207
|
+
}
|
|
208
|
+
getGoActionLifecycle(goId, actionKey) {
|
|
209
|
+
this.ensureGoIntegrationSchema();
|
|
210
|
+
return this.db.prepare('SELECT * FROM go_action_lifecycle WHERE go_id=? AND action_key=?').get(goId, actionKey);
|
|
211
|
+
}
|
|
212
|
+
recordGoActionOutcome(goId, actionKey, state, receipt) {
|
|
213
|
+
this.ensureGoIntegrationSchema();
|
|
214
|
+
this.db.prepare('UPDATE go_action_lifecycle SET state=?,receipt_json=? WHERE go_id=? AND action_key=? AND state!=?').run(state, receipt === undefined ? '' : JSON.stringify(receipt), goId, actionKey, 'accepted');
|
|
215
|
+
}
|
|
86
216
|
observeRunError(botName, chatId, provenance) {
|
|
87
217
|
const noticeId = errorIdentity(provenance.sessionKey, provenance.runId);
|
|
88
218
|
const previous = this.getErrorNotice(botName, chatId, noticeId);
|
|
@@ -1199,8 +1329,8 @@ export class MessageStore {
|
|
|
1199
1329
|
const now = Date.now();
|
|
1200
1330
|
const r = this.db.prepare(`INSERT INTO go_jobs
|
|
1201
1331
|
(bot_name, chat_id, goal, acceptance, state, executor_session_key, executor_session_id, checker_session_key, checker_model, revision, created_at, updated_at)
|
|
1202
|
-
VALUES (?, ?, ?, ?,
|
|
1203
|
-
.run(j.botName, j.chatId, j.goal, j.acceptance, j.executorSessionKey, j.executorSessionId, j.checkerSessionKey, j.checkerModel, now, now);
|
|
1332
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)`)
|
|
1333
|
+
.run(j.botName, j.chatId, j.goal, j.acceptance, j.state || "WAITING_WORK", j.executorSessionKey, j.executorSessionId, j.checkerSessionKey, j.checkerModel, now, now);
|
|
1204
1334
|
return Number(r.lastInsertRowid);
|
|
1205
1335
|
}
|
|
1206
1336
|
rowToGoJob(row) {
|
|
@@ -1278,10 +1408,15 @@ export class MessageStore {
|
|
|
1278
1408
|
}
|
|
1279
1409
|
/** Idempotent action claim: returns true when newly claimed (caller performs the action), false when it already existed. */
|
|
1280
1410
|
claimGoAction(goId, actionKey, kind, payload) {
|
|
1411
|
+
this.recordGoActionAttempt(goId, actionKey);
|
|
1281
1412
|
return this.db.prepare(`INSERT OR IGNORE INTO go_actions (go_id, action_key, kind, payload, done, created_at) VALUES (?, ?, ?, ?, 0, ?)`)
|
|
1282
1413
|
.run(goId, actionKey, kind, payload, Date.now()).changes === 1;
|
|
1283
1414
|
}
|
|
1415
|
+
hasUnresolvedGoActions(goId) {
|
|
1416
|
+
return !!this.db.prepare(`SELECT 1 FROM go_actions WHERE go_id=? AND done=0 LIMIT 1`).get(goId) || this.listUnresolvedGoSends(goId).length > 0 || this.hasUnknownGoRunResults(goId);
|
|
1417
|
+
}
|
|
1284
1418
|
completeGoAction(goId, actionKey, receipt) {
|
|
1419
|
+
this.recordGoActionOutcome(goId, actionKey, "accepted", receipt);
|
|
1285
1420
|
this.db.prepare(`UPDATE go_actions SET done=1, receipt=? WHERE go_id=? AND action_key=?`).run(receipt.slice(0, 500), goId, actionKey);
|
|
1286
1421
|
}
|
|
1287
1422
|
setGoModelPref(botName, chatId, model) {
|
|
@@ -1293,7 +1428,12 @@ export class MessageStore {
|
|
|
1293
1428
|
const r = this.db.prepare(`SELECT model FROM go_model_prefs WHERE bot_name=? AND chat_id=?`).get(botName, chatId);
|
|
1294
1429
|
return r?.model ?? null;
|
|
1295
1430
|
}
|
|
1431
|
+
closeObservers = new Set();
|
|
1432
|
+
onClose(callback) { this.closeObservers.add(callback); return () => this.closeObservers.delete(callback); }
|
|
1296
1433
|
close() {
|
|
1434
|
+
for (const observer of this.closeObservers)
|
|
1435
|
+
observer();
|
|
1436
|
+
this.closeObservers.clear();
|
|
1297
1437
|
this.db.close();
|
|
1298
1438
|
}
|
|
1299
1439
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { GoExecutorResult } from "./go-execution.js";
|
|
1
2
|
import { type ErrorProvenance, type ProactiveMessageMeta } from "./terminal-errors.js";
|
|
2
3
|
import { SessionWaitPaused, type SessionRuntimeStatus } from "./session-status.js";
|
|
3
4
|
import { OpenClawConfig } from "./config.js";
|
|
@@ -33,6 +34,12 @@ export declare const GATEWAY_PROTOCOL_MAX = 4;
|
|
|
33
34
|
* Full agent pipeline — tools, memory, skills, context management by OpenClaw.
|
|
34
35
|
*/
|
|
35
36
|
export declare class OpenClawClient {
|
|
37
|
+
private goEvidenceObservers;
|
|
38
|
+
/** Raw full event observer. Observation is NOT a Feishu delivery receipt. */
|
|
39
|
+
observeGoEvidence(callback: (event: {
|
|
40
|
+
channel: string;
|
|
41
|
+
payload: unknown;
|
|
42
|
+
}) => void): () => void;
|
|
36
43
|
private config;
|
|
37
44
|
private ws;
|
|
38
45
|
private pending;
|
|
@@ -236,6 +243,113 @@ export declare class OpenClawClient {
|
|
|
236
243
|
private releaseChatSendSlot;
|
|
237
244
|
private acquireMaintenanceSlot;
|
|
238
245
|
private releaseMaintenanceSlot;
|
|
246
|
+
/** Public native Goal protocol. Completion deliberately unavailable without host revision CAS. */
|
|
247
|
+
createGoNativeGoal(p: {
|
|
248
|
+
sessionKey: string;
|
|
249
|
+
sessionId: string;
|
|
250
|
+
objective: string;
|
|
251
|
+
operationId: string;
|
|
252
|
+
issuedAtMs: number;
|
|
253
|
+
}): Promise<any>;
|
|
254
|
+
readGoNativeGoal(sessionKey: string, sessionId: string): Promise<{
|
|
255
|
+
session: any;
|
|
256
|
+
goal: any;
|
|
257
|
+
revisionCAS: "unsupported";
|
|
258
|
+
}>;
|
|
259
|
+
updateGoNativeGoal(p: {
|
|
260
|
+
sessionKey: string;
|
|
261
|
+
sessionId: string;
|
|
262
|
+
goalId: string;
|
|
263
|
+
operationId: string;
|
|
264
|
+
issuedAtMs: number;
|
|
265
|
+
action: "edit" | "pause" | "resume" | "block";
|
|
266
|
+
objective?: string;
|
|
267
|
+
note?: string;
|
|
268
|
+
}): Promise<any>;
|
|
269
|
+
/** Admission receipt, not delivery. No restore, random retry key, or generation fallback. */
|
|
270
|
+
sendGoGuarded(p: {
|
|
271
|
+
sessionKey: string;
|
|
272
|
+
sessionId: string;
|
|
273
|
+
expectedLeafEntryId: string | null;
|
|
274
|
+
message: string;
|
|
275
|
+
idempotencyKey: string;
|
|
276
|
+
}): Promise<{
|
|
277
|
+
runId: any;
|
|
278
|
+
sessionId: string;
|
|
279
|
+
idempotencyKey: string;
|
|
280
|
+
raw: any;
|
|
281
|
+
}>;
|
|
282
|
+
/** Checker collection accepts only the admitted run, never a session-key fallback. */
|
|
283
|
+
collectGoRun(p: {
|
|
284
|
+
runId: string;
|
|
285
|
+
sessionKey: string;
|
|
286
|
+
sessionId?: string;
|
|
287
|
+
timeoutMs: number;
|
|
288
|
+
}): Promise<string>;
|
|
289
|
+
/** Public committed terminalReply is the authority, including explicit empty
|
|
290
|
+
* and terminal error. Stream prefaces, lifecycle end alone and timeout are not. */
|
|
291
|
+
readGoExecutorRunResult(p: {
|
|
292
|
+
sessionKey: string;
|
|
293
|
+
sessionId: string;
|
|
294
|
+
runId: string;
|
|
295
|
+
}, waitMs?: number): Promise<GoExecutorResult | null>;
|
|
296
|
+
collectGoExecutorRun(p: {
|
|
297
|
+
sessionKey: string;
|
|
298
|
+
sessionId: string;
|
|
299
|
+
runId: string;
|
|
300
|
+
timeoutMs: number;
|
|
301
|
+
}): Promise<GoExecutorResult>;
|
|
302
|
+
readGoRunResult(p: {
|
|
303
|
+
sessionKey: string;
|
|
304
|
+
sessionId: string;
|
|
305
|
+
runId: string;
|
|
306
|
+
}): Promise<string | null>;
|
|
307
|
+
/** Public generation-pinned original input receipts; absence is unknown, not permission to resend. */
|
|
308
|
+
reconcileGoSend(p: {
|
|
309
|
+
sessionKey: string;
|
|
310
|
+
sessionId: string;
|
|
311
|
+
idempotencyKey: string;
|
|
312
|
+
}): Promise<{
|
|
313
|
+
runId: any;
|
|
314
|
+
sessionId: string;
|
|
315
|
+
raw: any;
|
|
316
|
+
} | null>;
|
|
317
|
+
bindGoPolicy(binding: Record<string, string>): Promise<any>;
|
|
318
|
+
/** History publicly projects active leaf/run/recovery; describe alone does not. */
|
|
319
|
+
getGoSessionInfo(sessionKey: string): Promise<{
|
|
320
|
+
session: any;
|
|
321
|
+
}>;
|
|
322
|
+
verifyGoSafety(_executorKey: string): Promise<{
|
|
323
|
+
checkerToolsIsolated: boolean;
|
|
324
|
+
exactTaskAndDeliveryGate: boolean;
|
|
325
|
+
guardedSend: boolean;
|
|
326
|
+
}>;
|
|
327
|
+
initializeGoRuntime(p: {
|
|
328
|
+
goId: number;
|
|
329
|
+
goal: string;
|
|
330
|
+
executorSessionKey: string;
|
|
331
|
+
executorSessionId: string;
|
|
332
|
+
checkerSessionKey: string;
|
|
333
|
+
checkerSessionId: string;
|
|
334
|
+
}): Promise<{
|
|
335
|
+
executorSessionId: string;
|
|
336
|
+
checkerSessionId: string;
|
|
337
|
+
checkerRunId: string;
|
|
338
|
+
executorRunIds: never[];
|
|
339
|
+
}>;
|
|
340
|
+
readGoRuntimeSnapshot(sessionKey: string, sessionId: string): Promise<{
|
|
341
|
+
sessionKey: string;
|
|
342
|
+
sessionId: string;
|
|
343
|
+
archived: boolean;
|
|
344
|
+
complete: boolean;
|
|
345
|
+
independent: boolean;
|
|
346
|
+
atomicSnapshot: boolean;
|
|
347
|
+
queryAvailable: boolean;
|
|
348
|
+
running: any;
|
|
349
|
+
activeRunIds: any;
|
|
350
|
+
queuedInputs: any;
|
|
351
|
+
tasks: any;
|
|
352
|
+
}>;
|
|
239
353
|
chatSend(params: {
|
|
240
354
|
sessionKey: string;
|
|
241
355
|
message: string;
|