principles-disciple 1.6.0 → 1.7.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/dist/commands/context.js +7 -3
- package/dist/commands/evolution-status.d.ts +4 -0
- package/dist/commands/evolution-status.js +134 -0
- package/dist/commands/export.d.ts +2 -0
- package/dist/commands/export.js +45 -0
- package/dist/commands/focus.js +9 -6
- package/dist/commands/pain.js +8 -0
- package/dist/commands/principle-rollback.d.ts +4 -0
- package/dist/commands/principle-rollback.js +22 -0
- package/dist/commands/rollback.js +9 -3
- package/dist/commands/samples.d.ts +2 -0
- package/dist/commands/samples.js +55 -0
- package/dist/commands/trust.js +64 -81
- package/dist/core/config.d.ts +5 -0
- package/dist/core/control-ui-db.d.ts +68 -0
- package/dist/core/control-ui-db.js +274 -0
- package/dist/core/detection-funnel.d.ts +1 -1
- package/dist/core/detection-funnel.js +4 -0
- package/dist/core/dictionary.d.ts +2 -0
- package/dist/core/dictionary.js +13 -0
- package/dist/core/event-log.d.ts +7 -1
- package/dist/core/event-log.js +10 -0
- package/dist/core/evolution-engine.d.ts +5 -5
- package/dist/core/evolution-engine.js +18 -18
- package/dist/core/evolution-migration.d.ts +5 -0
- package/dist/core/evolution-migration.js +65 -0
- package/dist/core/evolution-reducer.d.ts +69 -0
- package/dist/core/evolution-reducer.js +369 -0
- package/dist/core/evolution-types.d.ts +103 -0
- package/dist/core/path-resolver.js +75 -36
- package/dist/core/paths.d.ts +7 -8
- package/dist/core/paths.js +48 -40
- package/dist/core/profile.js +1 -1
- package/dist/core/session-tracker.d.ts +14 -2
- package/dist/core/session-tracker.js +75 -9
- package/dist/core/thinking-models.d.ts +38 -0
- package/dist/core/thinking-models.js +170 -0
- package/dist/core/trajectory.d.ts +184 -0
- package/dist/core/trajectory.js +817 -0
- package/dist/core/trust-engine.d.ts +6 -0
- package/dist/core/trust-engine.js +50 -29
- package/dist/core/workspace-context.d.ts +13 -0
- package/dist/core/workspace-context.js +50 -7
- package/dist/hooks/gate.js +171 -87
- package/dist/hooks/llm.js +119 -71
- package/dist/hooks/pain.js +105 -5
- package/dist/hooks/prompt.d.ts +11 -14
- package/dist/hooks/prompt.js +283 -57
- package/dist/hooks/subagent.js +69 -28
- package/dist/hooks/trajectory-collector.d.ts +32 -0
- package/dist/hooks/trajectory-collector.js +256 -0
- package/dist/http/principles-console-route.d.ts +2 -0
- package/dist/http/principles-console-route.js +257 -0
- package/dist/i18n/commands.js +16 -0
- package/dist/index.js +105 -4
- package/dist/service/control-ui-query-service.d.ts +217 -0
- package/dist/service/control-ui-query-service.js +537 -0
- package/dist/service/empathy-observer-manager.d.ts +2 -0
- package/dist/service/empathy-observer-manager.js +43 -1
- package/dist/service/evolution-worker.d.ts +27 -0
- package/dist/service/evolution-worker.js +256 -41
- package/dist/service/runtime-summary-service.d.ts +79 -0
- package/dist/service/runtime-summary-service.js +319 -0
- package/dist/service/trajectory-service.d.ts +2 -0
- package/dist/service/trajectory-service.js +15 -0
- package/dist/tools/agent-spawn.d.ts +27 -6
- package/dist/tools/agent-spawn.js +339 -87
- package/dist/tools/deep-reflect.d.ts +27 -7
- package/dist/tools/deep-reflect.js +210 -121
- package/dist/types/event-types.d.ts +10 -2
- package/dist/types.d.ts +10 -0
- package/dist/types.js +5 -0
- package/openclaw.plugin.json +43 -11
- package/package.json +14 -4
- package/templates/langs/zh/skills/pd-daily/SKILL.md +97 -13
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { readPainFlagData } from '../core/pain.js';
|
|
4
|
+
import { resolvePdPath } from '../core/paths.js';
|
|
5
|
+
import { listSessions } from '../core/session-tracker.js';
|
|
6
|
+
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
7
|
+
const MAX_SOURCE_EVENTS = 5;
|
|
8
|
+
const LEGACY_TRUST_REWARD_POLICY = 'frozen_all_positive';
|
|
9
|
+
const GFI_PARTIAL_WARNING = 'GFI source attribution remains partial in Phase 2b because only the empathy slice is source-attributed; most non-empathy friction still lacks full per-source attribution.';
|
|
10
|
+
const DAILY_GFI_WARNING = 'daily-stats.gfi is not authoritative in Phase 1 and is used only as a fallback reference.';
|
|
11
|
+
const EVENT_BUFFER_WARNING = 'Live event buffer is unavailable in this context, so status may lag until events.jsonl flushes.';
|
|
12
|
+
function pushWarning(warnings, message) {
|
|
13
|
+
if (!warnings.includes(message)) {
|
|
14
|
+
warnings.push(message);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class RuntimeSummaryService {
|
|
18
|
+
static getSummary(workspaceDir, options) {
|
|
19
|
+
const generatedAt = new Date().toISOString();
|
|
20
|
+
const warnings = [];
|
|
21
|
+
const wctx = WorkspaceContext.fromHookContext({ workspaceDir });
|
|
22
|
+
const sessions = this.mergeSessionSnapshots(this.readSessions(wctx.resolve('SESSION_DIR'), warnings), workspaceDir);
|
|
23
|
+
const selectedSession = this.selectSession(sessions, options?.sessionId ?? null);
|
|
24
|
+
const selectedSessionId = selectedSession.session?.sessionId ?? null;
|
|
25
|
+
const persistedEvents = this.readEvents(path.join(wctx.stateDir, 'logs', 'events.jsonl'), warnings);
|
|
26
|
+
const hasBufferedEventAccess = typeof wctx.eventLog.getBufferedEvents === 'function';
|
|
27
|
+
const bufferedEvents = hasBufferedEventAccess
|
|
28
|
+
? wctx.eventLog.getBufferedEvents()
|
|
29
|
+
: [];
|
|
30
|
+
const events = [...persistedEvents, ...bufferedEvents];
|
|
31
|
+
const dailyStats = this.readJsonFile(path.join(wctx.stateDir, 'logs', 'daily-stats.json'), warnings, false);
|
|
32
|
+
const today = generatedAt.slice(0, 10);
|
|
33
|
+
const dailyGfiPeak = dailyStats?.[today]?.gfi?.peak;
|
|
34
|
+
const gfiCurrent = selectedSession.session && Number.isFinite(selectedSession.session.currentGfi)
|
|
35
|
+
? Number(selectedSession.session.currentGfi)
|
|
36
|
+
: null;
|
|
37
|
+
const sessionPeak = selectedSession.session && Number.isFinite(selectedSession.session.dailyGfiPeak)
|
|
38
|
+
? Number(selectedSession.session.dailyGfiPeak)
|
|
39
|
+
: null;
|
|
40
|
+
const gfiPeak = sessionPeak ?? (Number.isFinite(dailyGfiPeak) ? Number(dailyGfiPeak) : null);
|
|
41
|
+
pushWarning(warnings, GFI_PARTIAL_WARNING);
|
|
42
|
+
if (sessionPeak === null && Number.isFinite(dailyGfiPeak)) {
|
|
43
|
+
pushWarning(warnings, DAILY_GFI_WARNING);
|
|
44
|
+
}
|
|
45
|
+
if (!hasBufferedEventAccess) {
|
|
46
|
+
pushWarning(warnings, EVENT_BUFFER_WARNING);
|
|
47
|
+
}
|
|
48
|
+
if (!selectedSession.session) {
|
|
49
|
+
pushWarning(warnings, 'No persisted session state was found; current session GFI is unavailable.');
|
|
50
|
+
}
|
|
51
|
+
const queue = this.readJsonFile(wctx.resolve('EVOLUTION_QUEUE'), warnings, false);
|
|
52
|
+
const directive = this.readJsonFile(wctx.resolve('EVOLUTION_DIRECTIVE'), warnings, false);
|
|
53
|
+
const queueStats = this.buildQueueStats(queue);
|
|
54
|
+
const directiveSummary = this.buildDirectiveSummary(directive, generatedAt, warnings, queueStats);
|
|
55
|
+
const painFlag = readPainFlagData(workspaceDir);
|
|
56
|
+
const painCandidates = this.readJsonFile(wctx.resolve('PAIN_CANDIDATES'), warnings, false);
|
|
57
|
+
const legacyTrust = this.readLegacyTrust(resolvePdPath(workspaceDir, 'AGENT_SCORECARD'), wctx, warnings);
|
|
58
|
+
const lastPainSignal = this.findLastPainSignal(events, selectedSessionId);
|
|
59
|
+
const gfiSources = this.buildGfiSources(events, selectedSessionId);
|
|
60
|
+
const gateStats = this.buildGateStats(events, selectedSessionId, warnings);
|
|
61
|
+
return {
|
|
62
|
+
gfi: {
|
|
63
|
+
current: gfiCurrent,
|
|
64
|
+
peak: gfiPeak,
|
|
65
|
+
sources: gfiSources,
|
|
66
|
+
dataQuality: 'partial',
|
|
67
|
+
},
|
|
68
|
+
legacyTrust,
|
|
69
|
+
evolution: {
|
|
70
|
+
queue: queueStats,
|
|
71
|
+
directive: directiveSummary,
|
|
72
|
+
dataQuality: queue ? 'authoritative' : 'partial',
|
|
73
|
+
},
|
|
74
|
+
pain: {
|
|
75
|
+
activeFlag: Object.keys(painFlag).length > 0,
|
|
76
|
+
activeFlagSource: painFlag.source || null,
|
|
77
|
+
candidates: painCandidates?.candidates && typeof painCandidates.candidates === 'object'
|
|
78
|
+
? Object.keys(painCandidates.candidates).length
|
|
79
|
+
: null,
|
|
80
|
+
lastSignal: lastPainSignal,
|
|
81
|
+
},
|
|
82
|
+
gate: gateStats,
|
|
83
|
+
metadata: {
|
|
84
|
+
generatedAt,
|
|
85
|
+
workspaceDir,
|
|
86
|
+
sessionId: selectedSessionId,
|
|
87
|
+
selectedSessionReason: selectedSession.reason,
|
|
88
|
+
warnings,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
static readSessions(sessionDir, warnings) {
|
|
93
|
+
if (!fs.existsSync(sessionDir)) {
|
|
94
|
+
pushWarning(warnings, 'No persisted session directory exists yet; session-scoped runtime state is unavailable.');
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
const sessions = [];
|
|
98
|
+
for (const file of fs.readdirSync(sessionDir)) {
|
|
99
|
+
if (!file.endsWith('.json'))
|
|
100
|
+
continue;
|
|
101
|
+
try {
|
|
102
|
+
const raw = fs.readFileSync(path.join(sessionDir, file), 'utf8');
|
|
103
|
+
const parsed = JSON.parse(raw);
|
|
104
|
+
if (parsed?.sessionId) {
|
|
105
|
+
sessions.push(parsed);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
pushWarning(warnings, `Failed to parse session snapshot: ${file}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return sessions.sort((a, b) => (b.lastActivityAt ?? 0) - (a.lastActivityAt ?? 0));
|
|
113
|
+
}
|
|
114
|
+
static selectSession(sessions, explicitSessionId) {
|
|
115
|
+
if (explicitSessionId) {
|
|
116
|
+
const explicit = sessions.find((session) => session.sessionId === explicitSessionId) ?? null;
|
|
117
|
+
return { session: explicit, reason: explicit ? 'explicit' : 'none' };
|
|
118
|
+
}
|
|
119
|
+
if (sessions.length === 0) {
|
|
120
|
+
return { session: null, reason: 'none' };
|
|
121
|
+
}
|
|
122
|
+
return { session: sessions[0], reason: 'latest_active' };
|
|
123
|
+
}
|
|
124
|
+
static mergeSessionSnapshots(persistedSessions, workspaceDir) {
|
|
125
|
+
const merged = new Map();
|
|
126
|
+
for (const session of persistedSessions) {
|
|
127
|
+
merged.set(session.sessionId, { ...session });
|
|
128
|
+
}
|
|
129
|
+
for (const live of listSessions(workspaceDir)) {
|
|
130
|
+
const persisted = merged.get(live.sessionId);
|
|
131
|
+
merged.set(live.sessionId, {
|
|
132
|
+
sessionId: live.sessionId,
|
|
133
|
+
currentGfi: Number.isFinite(live.currentGfi) ? Number(live.currentGfi) : persisted?.currentGfi,
|
|
134
|
+
dailyGfiPeak: Number.isFinite(live.dailyGfiPeak) ? Number(live.dailyGfiPeak) : persisted?.dailyGfiPeak,
|
|
135
|
+
lastActivityAt: Number.isFinite(live.lastActivityAt) ? Number(live.lastActivityAt) : persisted?.lastActivityAt,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return [...merged.values()].sort((a, b) => (b.lastActivityAt ?? 0) - (a.lastActivityAt ?? 0));
|
|
139
|
+
}
|
|
140
|
+
static buildQueueStats(queue) {
|
|
141
|
+
const stats = { pending: 0, inProgress: 0, completed: 0 };
|
|
142
|
+
if (!queue)
|
|
143
|
+
return stats;
|
|
144
|
+
for (const item of queue) {
|
|
145
|
+
if (item?.status === 'completed') {
|
|
146
|
+
stats.completed++;
|
|
147
|
+
}
|
|
148
|
+
else if (item?.status === 'in_progress') {
|
|
149
|
+
stats.inProgress++;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
stats.pending++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return stats;
|
|
156
|
+
}
|
|
157
|
+
static buildDirectiveSummary(directive, generatedAt, warnings, queueStats) {
|
|
158
|
+
if (!directive) {
|
|
159
|
+
return {
|
|
160
|
+
exists: false,
|
|
161
|
+
active: null,
|
|
162
|
+
ageSeconds: null,
|
|
163
|
+
taskPreview: null,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const timestampMs = directive.timestamp ? new Date(directive.timestamp).getTime() : NaN;
|
|
167
|
+
const ageSeconds = Number.isFinite(timestampMs)
|
|
168
|
+
? Math.max(0, Math.floor((new Date(generatedAt).getTime() - timestampMs) / 1000))
|
|
169
|
+
: null;
|
|
170
|
+
if (directive.active && queueStats.pending === 0 && queueStats.inProgress === 0) {
|
|
171
|
+
warnings.push('Directive is active while the queue has no pending or in-progress task; worker state may be stale.');
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
exists: true,
|
|
175
|
+
active: typeof directive.active === 'boolean' ? directive.active : null,
|
|
176
|
+
ageSeconds,
|
|
177
|
+
taskPreview: directive.task ? directive.task.slice(0, 160) : null,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
static readLegacyTrust(scorecardPath, wctx, warnings) {
|
|
181
|
+
const scorecard = this.readJsonFile(scorecardPath, warnings, false);
|
|
182
|
+
const score = Number.isFinite(scorecard?.trust_score) ? Number(scorecard?.trust_score) : null;
|
|
183
|
+
const settings = wctx.config.get('trust');
|
|
184
|
+
const stageThresholds = settings?.stages ?? {
|
|
185
|
+
stage_1_observer: 30,
|
|
186
|
+
stage_2_editor: 60,
|
|
187
|
+
stage_3_developer: 80,
|
|
188
|
+
};
|
|
189
|
+
let stage = null;
|
|
190
|
+
if (score !== null) {
|
|
191
|
+
if (score < (stageThresholds.stage_1_observer ?? 30)) {
|
|
192
|
+
stage = 1;
|
|
193
|
+
}
|
|
194
|
+
else if (score < (stageThresholds.stage_2_editor ?? 60)) {
|
|
195
|
+
stage = 2;
|
|
196
|
+
}
|
|
197
|
+
else if (score < (stageThresholds.stage_3_developer ?? 80)) {
|
|
198
|
+
stage = 3;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
stage = 4;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
score,
|
|
206
|
+
stage,
|
|
207
|
+
frozen: true,
|
|
208
|
+
lastUpdated: scorecard?.last_updated ?? null,
|
|
209
|
+
rewardPolicy: LEGACY_TRUST_REWARD_POLICY,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
static readEvents(eventsPath, warnings) {
|
|
213
|
+
if (!fs.existsSync(eventsPath)) {
|
|
214
|
+
warnings.push('No events.jsonl file exists yet; recent pain and gate summaries are partial.');
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const raw = fs.readFileSync(eventsPath, 'utf8').trim();
|
|
219
|
+
if (!raw)
|
|
220
|
+
return [];
|
|
221
|
+
let parseFailures = 0;
|
|
222
|
+
const entries = raw
|
|
223
|
+
.split('\n')
|
|
224
|
+
.map((line) => {
|
|
225
|
+
try {
|
|
226
|
+
return JSON.parse(line);
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
parseFailures += 1;
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
.filter((entry) => entry !== null);
|
|
234
|
+
if (parseFailures > 0) {
|
|
235
|
+
pushWarning(warnings, `Skipped ${parseFailures} malformed event line${parseFailures === 1 ? '' : 's'} while reading events.jsonl.`);
|
|
236
|
+
}
|
|
237
|
+
return entries;
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
pushWarning(warnings, 'Failed to read events.jsonl; recent pain and gate summaries are partial.');
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
static buildGfiSources(events, sessionId) {
|
|
245
|
+
const filtered = events
|
|
246
|
+
.filter((entry) => {
|
|
247
|
+
if (sessionId && entry.sessionId !== sessionId)
|
|
248
|
+
return false;
|
|
249
|
+
return (entry.type === 'pain_signal' ||
|
|
250
|
+
(entry.type === 'tool_call' && entry.category === 'failure'));
|
|
251
|
+
})
|
|
252
|
+
.slice(-MAX_SOURCE_EVENTS)
|
|
253
|
+
.reverse();
|
|
254
|
+
return filtered.map((entry) => {
|
|
255
|
+
if (entry.type === 'pain_signal') {
|
|
256
|
+
return {
|
|
257
|
+
source: String(entry.data?.source ?? 'pain_signal'),
|
|
258
|
+
score: this.asFiniteNumber(entry.data?.score),
|
|
259
|
+
ts: entry.ts,
|
|
260
|
+
confidence: this.asFiniteNumber(entry.data?.confidence),
|
|
261
|
+
origin: typeof entry.data?.origin === 'string' ? entry.data.origin : undefined,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
source: `tool_failure:${String(entry.data?.toolName ?? 'unknown')}`,
|
|
266
|
+
score: this.asFiniteNumber(entry.data?.gfi),
|
|
267
|
+
ts: entry.ts,
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
static findLastPainSignal(events, sessionId) {
|
|
272
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
273
|
+
const entry = events[i];
|
|
274
|
+
if (entry.type !== 'pain_signal')
|
|
275
|
+
continue;
|
|
276
|
+
if (sessionId && entry.sessionId !== sessionId)
|
|
277
|
+
continue;
|
|
278
|
+
return {
|
|
279
|
+
source: String(entry.data?.source ?? 'pain_signal'),
|
|
280
|
+
ts: entry.ts ?? null,
|
|
281
|
+
reason: typeof entry.data?.reason === 'string' ? entry.data.reason : null,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
static buildGateStats(events, sessionId, warnings) {
|
|
287
|
+
const scoped = events.filter((entry) => {
|
|
288
|
+
if (sessionId && entry.sessionId !== sessionId)
|
|
289
|
+
return false;
|
|
290
|
+
return entry.type === 'gate_block' || entry.type === 'gate_bypass';
|
|
291
|
+
});
|
|
292
|
+
if (scoped.length === 0) {
|
|
293
|
+
pushWarning(warnings, 'Gate block counts before Phase 1 may be incomplete because older block events were not recorded to event-log.');
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
recentBlocks: scoped.filter((entry) => entry.type === 'gate_block').length,
|
|
297
|
+
recentBypasses: scoped.filter((entry) => entry.type === 'gate_bypass').length,
|
|
298
|
+
dataQuality: scoped.length > 0 ? 'authoritative' : 'partial',
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
static readJsonFile(filePath, warnings, warnOnMissing) {
|
|
302
|
+
if (!fs.existsSync(filePath)) {
|
|
303
|
+
if (warnOnMissing) {
|
|
304
|
+
pushWarning(warnings, `Missing expected file: ${path.basename(filePath)}`);
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
pushWarning(warnings, `Failed to parse ${path.basename(filePath)}.`);
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
static asFiniteNumber(value) {
|
|
317
|
+
return Number.isFinite(value) ? Number(value) : undefined;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { TrajectoryRegistry } from '../core/trajectory.js';
|
|
2
|
+
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
3
|
+
export const TrajectoryService = {
|
|
4
|
+
id: 'principles-disciple-trajectory',
|
|
5
|
+
start(ctx) {
|
|
6
|
+
if (!ctx.workspaceDir)
|
|
7
|
+
return;
|
|
8
|
+
WorkspaceContext.fromHookContext(ctx).trajectory;
|
|
9
|
+
},
|
|
10
|
+
stop(ctx) {
|
|
11
|
+
if (!ctx.workspaceDir)
|
|
12
|
+
return;
|
|
13
|
+
TrajectoryRegistry.dispose(ctx.workspaceDir);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -6,22 +6,43 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { OpenClawPluginApi } from '../openclaw-sdk.js';
|
|
8
8
|
/**
|
|
9
|
-
* Agent Spawn Tool
|
|
9
|
+
* Create Agent Spawn Tool
|
|
10
|
+
*
|
|
11
|
+
* Uses factory pattern to capture `api` in closure, following OpenClaw plugin SDK conventions.
|
|
12
|
+
* The execute signature must be: async (_toolCallId: string, rawParams: Record<string, unknown>)
|
|
10
13
|
*/
|
|
11
|
-
export declare
|
|
14
|
+
export declare function createAgentSpawnTool(api: OpenClawPluginApi): {
|
|
12
15
|
name: string;
|
|
13
16
|
description: string;
|
|
14
17
|
parameters: import("@sinclair/typebox").TObject<{
|
|
15
18
|
agentType: import("@sinclair/typebox").TString;
|
|
16
19
|
task: import("@sinclair/typebox").TString;
|
|
20
|
+
runInBackground: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
17
21
|
}>;
|
|
18
22
|
/**
|
|
19
23
|
* Execution logic for the agent spawn tool
|
|
24
|
+
*
|
|
25
|
+
* OpenClaw tool execute signature:
|
|
26
|
+
* - First parameter: _toolCallId (string) - the tool call ID
|
|
27
|
+
* - Second parameter: rawParams (Record<string, unknown>) - the actual parameters
|
|
28
|
+
* - Third parameter (optional): signal (AbortSignal) - for cancellation
|
|
20
29
|
*/
|
|
21
|
-
execute(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
execute(_toolCallId: string, rawParams: Record<string, unknown>): Promise<{
|
|
31
|
+
content: Array<{
|
|
32
|
+
type: string;
|
|
33
|
+
text: string;
|
|
34
|
+
}>;
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
37
|
+
export declare const agentSpawnTool: {
|
|
38
|
+
name: string;
|
|
39
|
+
description: string;
|
|
40
|
+
parameters: import("@sinclair/typebox").TObject<{
|
|
41
|
+
agentType: import("@sinclair/typebox").TString;
|
|
42
|
+
task: import("@sinclair/typebox").TString;
|
|
43
|
+
runInBackground: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
44
|
+
}>;
|
|
45
|
+
execute: () => never;
|
|
25
46
|
};
|
|
26
47
|
/**
|
|
27
48
|
* Batch spawn multiple agents in sequence
|