@karmaniverous/jeeves-meta 0.15.12 → 0.16.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 +1 -1
- package/dist/cli/jeeves-meta/architect.md +1 -4
- package/dist/cli/jeeves-meta/index.js +115 -69
- package/dist/executor/GatewayExecutor.d.ts +20 -1
- package/dist/index.js +115 -75
- package/dist/interfaces/MetaExecutor.d.ts +0 -2
- package/dist/prompts/architect.md +1 -4
- package/dist/routes/metasUpdate.d.ts +1 -1
- package/dist/schema/config.d.ts +0 -9
- package/dist/schema/meta.d.ts +0 -3
- package/dist/seed/createMeta.d.ts +0 -6
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ jeeves-meta service install --config /path/to/jeeves-meta/config.json
|
|
|
66
66
|
| POST | `/config/apply` | Apply a config patch (merge or replace) |
|
|
67
67
|
| GET | `/queue` | Queue state: current (with phase), overrides, automatic, pending |
|
|
68
68
|
| POST | `/queue/clear` | Remove all override queue entries |
|
|
69
|
-
| PATCH | `/metas/:path` | Update user-settable reserved properties (`_steer`, `_emphasis`, `_depth`, `_crossRefs`, `_disabled
|
|
69
|
+
| PATCH | `/metas/:path` | Update user-settable reserved properties (`_steer`, `_emphasis`, `_depth`, `_crossRefs`, `_disabled`) |
|
|
70
70
|
|
|
71
71
|
## Configuration
|
|
72
72
|
|
|
@@ -128,8 +128,6 @@ The Builder should:
|
|
|
128
128
|
4. Merge new findings with previous `_content` (carried in context)
|
|
129
129
|
|
|
130
130
|
If the scope is small enough to process in one pass, omit chunking instructions.
|
|
131
|
-
The Builder has a timeout of \{{config.builderTimeout}} seconds.
|
|
132
|
-
|
|
133
131
|
### 8. Output Structure
|
|
134
132
|
|
|
135
133
|
Define non-underscore fields for structured data and the _content narrative
|
|
@@ -148,7 +146,6 @@ Quote the specific issue and state what to do differently.
|
|
|
148
146
|
Your task brief will be compiled as a Handlebars template before the Builder
|
|
149
147
|
receives it. You can use these variables to write adaptive instructions:
|
|
150
148
|
|
|
151
|
-
- `\{{config.builderTimeout}}` — Builder timeout in seconds
|
|
152
149
|
- `\{{config.maxLines}}` — Maximum _content lines
|
|
153
150
|
- `\{{config.architectEvery}}` — Cycles between architect refreshes
|
|
154
151
|
- `\{{config.maxArchive}}` — Archive snapshots retained
|
|
@@ -159,7 +156,7 @@ receives it. You can use these variables to write adaptive instructions:
|
|
|
159
156
|
- `\{{meta._depth}}` — Scheduling depth
|
|
160
157
|
- `\{{meta._emphasis}}` — Scheduling emphasis
|
|
161
158
|
|
|
162
|
-
Example: "Process files in chunks of 50.
|
|
159
|
+
Example: "Process files in chunks of 50. Limit output to \{{config.maxLines}} lines."
|
|
163
160
|
|
|
164
161
|
## Constraints
|
|
165
162
|
|
|
@@ -782,12 +782,6 @@ const autoSeedRuleSchema = z.object({
|
|
|
782
782
|
crossRefs: z.array(z.string()).optional(),
|
|
783
783
|
/** Walk up this many extra parent levels from the matched file's directory. Default 0. */
|
|
784
784
|
parentDepth: z.number().int().min(0).optional(),
|
|
785
|
-
/** Per-category timeout override for the architect phase (seconds, min 30). */
|
|
786
|
-
architectTimeout: z.number().int().min(30).optional(),
|
|
787
|
-
/** Per-category timeout override for the builder phase (seconds, min 30). */
|
|
788
|
-
builderTimeout: z.number().int().min(30).optional(),
|
|
789
|
-
/** Per-category timeout override for the critic phase (seconds, min 30). */
|
|
790
|
-
criticTimeout: z.number().int().min(30).optional(),
|
|
791
785
|
});
|
|
792
786
|
/** Zod schema for jeeves-meta service configuration (superset of MetaConfig). */
|
|
793
787
|
const serviceConfigSchema = metaConfigSchema.extend({
|
|
@@ -925,7 +919,7 @@ class SpawnTimeoutError extends Error {
|
|
|
925
919
|
* @module executor/GatewayExecutor
|
|
926
920
|
*/
|
|
927
921
|
const DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
928
|
-
const
|
|
922
|
+
const DEFAULT_SAFETY_VALVE_MS = 3_600_000; // 1 hour fallback
|
|
929
923
|
/**
|
|
930
924
|
* MetaExecutor that spawns OpenClaw sessions via the gateway's
|
|
931
925
|
* `/tools/invoke` endpoint.
|
|
@@ -956,6 +950,42 @@ class GatewayExecutor {
|
|
|
956
950
|
/* best-effort cleanup */
|
|
957
951
|
}
|
|
958
952
|
}
|
|
953
|
+
/** Read and clean up the staging output file. Returns content or undefined if absent. */
|
|
954
|
+
readStagingFile(outputPath) {
|
|
955
|
+
if (!existsSync(outputPath))
|
|
956
|
+
return undefined;
|
|
957
|
+
try {
|
|
958
|
+
return readFileSync(outputPath, 'utf8');
|
|
959
|
+
}
|
|
960
|
+
finally {
|
|
961
|
+
this.cleanupOutputFile(outputPath);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
/** Extract plain text from a message content field, skipping ANNOUNCE_SKIP sentinels. */
|
|
965
|
+
static extractMessageText(content) {
|
|
966
|
+
if (!content)
|
|
967
|
+
return undefined;
|
|
968
|
+
const text = typeof content === 'string'
|
|
969
|
+
? content
|
|
970
|
+
: Array.isArray(content)
|
|
971
|
+
? content
|
|
972
|
+
.filter((b) => b.type === 'text' && b.text)
|
|
973
|
+
.map((b) => b.text)
|
|
974
|
+
.join('\n')
|
|
975
|
+
: '';
|
|
976
|
+
return text && text.trim() !== 'ANNOUNCE_SKIP' ? text : undefined;
|
|
977
|
+
}
|
|
978
|
+
/** Check history messages for terminal completion. */
|
|
979
|
+
static checkHistoryCompletion(messages) {
|
|
980
|
+
if (messages.length === 0)
|
|
981
|
+
return { done: false, timedOut: false };
|
|
982
|
+
const last = messages[messages.length - 1];
|
|
983
|
+
if (last.role !== 'assistant' || !last.stopReason)
|
|
984
|
+
return { done: false, timedOut: false };
|
|
985
|
+
if (last.stopReason === 'toolUse' || last.stopReason === 'error')
|
|
986
|
+
return { done: false, timedOut: false };
|
|
987
|
+
return { done: true, timedOut: last.stopReason === 'timeout' };
|
|
988
|
+
}
|
|
959
989
|
/** Invoke a gateway tool via the /tools/invoke HTTP endpoint. */
|
|
960
990
|
async invoke(tool, args, sessionKey) {
|
|
961
991
|
const headers = {
|
|
@@ -982,7 +1012,13 @@ class GatewayExecutor {
|
|
|
982
1012
|
}
|
|
983
1013
|
return data;
|
|
984
1014
|
}
|
|
985
|
-
/**
|
|
1015
|
+
/**
|
|
1016
|
+
* Look up session metadata (tokens, completion status) via sessions_list.
|
|
1017
|
+
*
|
|
1018
|
+
* Detects gateway-side timeout (`status: "timeout"`) and killed sessions
|
|
1019
|
+
* (`status: "killed"`) as completed, with a `timedOut` flag to distinguish
|
|
1020
|
+
* timeout from normal completion.
|
|
1021
|
+
*/
|
|
986
1022
|
async getSessionInfo(sessionKey) {
|
|
987
1023
|
try {
|
|
988
1024
|
const result = await this.invoke('sessions_list', {
|
|
@@ -998,13 +1034,18 @@ class GatewayExecutor {
|
|
|
998
1034
|
// With limit=200 this is reliable; a false positive here only
|
|
999
1035
|
// means we read the output file slightly early (still correct
|
|
1000
1036
|
// if the file exists).
|
|
1001
|
-
return { completed: true };
|
|
1037
|
+
return { completed: true, timedOut: false };
|
|
1002
1038
|
}
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1039
|
+
const status = match.status;
|
|
1040
|
+
const done = status === 'completed' ||
|
|
1041
|
+
status === 'done' ||
|
|
1042
|
+
status === 'timeout' ||
|
|
1043
|
+
status === 'killed';
|
|
1044
|
+
const timedOut = status === 'timeout';
|
|
1045
|
+
return { tokens: match.totalTokens, completed: done, timedOut };
|
|
1005
1046
|
}
|
|
1006
1047
|
catch {
|
|
1007
|
-
return { completed: false };
|
|
1048
|
+
return { completed: false, timedOut: false };
|
|
1008
1049
|
}
|
|
1009
1050
|
}
|
|
1010
1051
|
/** Whether this executor has been aborted by the operator. */
|
|
@@ -1015,12 +1056,38 @@ class GatewayExecutor {
|
|
|
1015
1056
|
abort() {
|
|
1016
1057
|
this.controller.abort();
|
|
1017
1058
|
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Query the gateway's configured subagent run timeout.
|
|
1061
|
+
*
|
|
1062
|
+
* Returns the value in milliseconds, or `undefined` if the query fails
|
|
1063
|
+
* or the value is absent/zero (no timeout configured).
|
|
1064
|
+
*/
|
|
1065
|
+
async queryGatewayRunTimeout() {
|
|
1066
|
+
try {
|
|
1067
|
+
const result = await this.invoke('session_status', {});
|
|
1068
|
+
const details = (result.result?.details ?? result.result ?? {});
|
|
1069
|
+
const runTimeoutSeconds = details.runTimeoutSeconds ??
|
|
1070
|
+
details.timeout;
|
|
1071
|
+
if (typeof runTimeoutSeconds === 'number' && runTimeoutSeconds > 0) {
|
|
1072
|
+
return runTimeoutSeconds * 1000;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
catch {
|
|
1076
|
+
// Gateway unreachable or field not exposed — fall back to default
|
|
1077
|
+
}
|
|
1078
|
+
return undefined;
|
|
1079
|
+
}
|
|
1018
1080
|
async spawn(task, options) {
|
|
1019
1081
|
// Fresh controller for each spawn call
|
|
1020
1082
|
this.controller = new AbortController();
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1083
|
+
// Safety-valve deadline: gateway's runTimeoutSeconds + 60s buffer,
|
|
1084
|
+
// defaulting to 1 hour if the gateway value is 0/absent/query fails.
|
|
1085
|
+
// This is a circuit breaker, not a timeout mechanism.
|
|
1086
|
+
const gatewayTimeoutMs = await this.queryGatewayRunTimeout();
|
|
1087
|
+
const safetyValveMs = gatewayTimeoutMs
|
|
1088
|
+
? gatewayTimeoutMs + 60_000
|
|
1089
|
+
: DEFAULT_SAFETY_VALVE_MS;
|
|
1090
|
+
const safetyDeadline = Date.now() + safetyValveMs;
|
|
1024
1091
|
// Ensure workspace dir exists
|
|
1025
1092
|
if (!existsSync(this.workspaceDir)) {
|
|
1026
1093
|
mkdirSync(this.workspaceDir, { recursive: true });
|
|
@@ -1042,7 +1109,6 @@ class GatewayExecutor {
|
|
|
1042
1109
|
const spawnResult = await this.invoke('sessions_spawn', {
|
|
1043
1110
|
task: taskWithOutput,
|
|
1044
1111
|
label,
|
|
1045
|
-
runTimeoutSeconds: timeoutSeconds,
|
|
1046
1112
|
...(options?.thinking ? { thinking: options.thinking } : {}),
|
|
1047
1113
|
...(options?.model ? { model: options.model } : {}),
|
|
1048
1114
|
});
|
|
@@ -1054,9 +1120,18 @@ class GatewayExecutor {
|
|
|
1054
1120
|
throw new Error('Gateway sessions_spawn returned no sessionKey: ' +
|
|
1055
1121
|
JSON.stringify(spawnResult));
|
|
1056
1122
|
}
|
|
1057
|
-
// Step 2: Poll for completion
|
|
1123
|
+
// Step 2: Poll for completion — gateway owns the subagent lifecycle.
|
|
1124
|
+
// Loop exits via: (a) completion detection, (b) abort signal,
|
|
1125
|
+
// (c) gateway-side timeout detection, or (d) safety-valve circuit breaker.
|
|
1058
1126
|
await sleepAsync(3000);
|
|
1059
|
-
while (
|
|
1127
|
+
while (true) {
|
|
1128
|
+
// Safety-valve circuit breaker
|
|
1129
|
+
if (Date.now() >= safetyDeadline) {
|
|
1130
|
+
this.cleanupOutputFile(outputPath);
|
|
1131
|
+
throw new SpawnTimeoutError('Safety-valve deadline exceeded (' +
|
|
1132
|
+
safetyValveMs.toString() +
|
|
1133
|
+
'ms) — gateway timeout may be misconfigured', outputPath);
|
|
1134
|
+
}
|
|
1060
1135
|
// Check for abort before each poll iteration
|
|
1061
1136
|
if (this.controller.signal.aborted) {
|
|
1062
1137
|
this.cleanupOutputFile(outputPath);
|
|
@@ -1073,62 +1148,48 @@ class GatewayExecutor {
|
|
|
1073
1148
|
[];
|
|
1074
1149
|
const msgArray = messages;
|
|
1075
1150
|
// Check 1: terminal stop reason in history
|
|
1076
|
-
|
|
1077
|
-
if (msgArray.length > 0) {
|
|
1078
|
-
const lastMsg = msgArray[msgArray.length - 1];
|
|
1079
|
-
if (lastMsg.role === 'assistant' &&
|
|
1080
|
-
lastMsg.stopReason &&
|
|
1081
|
-
lastMsg.stopReason !== 'toolUse' &&
|
|
1082
|
-
lastMsg.stopReason !== 'error') {
|
|
1083
|
-
historyDone = true;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1151
|
+
const { done: historyDone, timedOut: historyTimedOut } = GatewayExecutor.checkHistoryCompletion(msgArray);
|
|
1086
1152
|
// Check 2: session completion status via sessions_list
|
|
1087
1153
|
const sessionInfo = await this.getSessionInfo(sessionKey);
|
|
1154
|
+
const timedOut = sessionInfo.timedOut || historyTimedOut;
|
|
1088
1155
|
if (historyDone || sessionInfo.completed) {
|
|
1089
1156
|
const tokens = sessionInfo.tokens;
|
|
1090
|
-
//
|
|
1091
|
-
if (
|
|
1092
|
-
|
|
1093
|
-
|
|
1157
|
+
// Gateway-side timeout detected — check staging file for recovery
|
|
1158
|
+
if (timedOut) {
|
|
1159
|
+
const output = this.readStagingFile(outputPath);
|
|
1160
|
+
if (output !== undefined)
|
|
1094
1161
|
return { output, tokens };
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
try {
|
|
1098
|
-
unlinkSync(outputPath);
|
|
1099
|
-
}
|
|
1100
|
-
catch {
|
|
1101
|
-
/* cleanup best-effort */
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1162
|
+
// No output or partial output — throw for _state recovery (§3.16.6)
|
|
1163
|
+
throw new SpawnTimeoutError('Gateway-side timeout detected (session status: timeout)', outputPath);
|
|
1104
1164
|
}
|
|
1165
|
+
// Normal completion — read output from file
|
|
1166
|
+
const output = this.readStagingFile(outputPath);
|
|
1167
|
+
if (output !== undefined)
|
|
1168
|
+
return { output, tokens };
|
|
1105
1169
|
// Fallback: extract from message content if file wasn't written.
|
|
1106
1170
|
// Skip ANNOUNCE_SKIP sentinel messages — the real output is in
|
|
1107
1171
|
// a preceding assistant message (the file write).
|
|
1108
1172
|
for (let i = msgArray.length - 1; i >= 0; i--) {
|
|
1109
1173
|
const msg = msgArray[i];
|
|
1110
|
-
if (msg.role === 'assistant'
|
|
1111
|
-
const text =
|
|
1112
|
-
|
|
1113
|
-
: Array.isArray(msg.content)
|
|
1114
|
-
? msg.content
|
|
1115
|
-
.filter((b) => b.type === 'text' && b.text)
|
|
1116
|
-
.map((b) => b.text)
|
|
1117
|
-
.join('\n')
|
|
1118
|
-
: '';
|
|
1119
|
-
if (text && text.trim() !== 'ANNOUNCE_SKIP')
|
|
1174
|
+
if (msg.role === 'assistant') {
|
|
1175
|
+
const text = GatewayExecutor.extractMessageText(msg.content);
|
|
1176
|
+
if (text !== undefined)
|
|
1120
1177
|
return { output: text, tokens };
|
|
1121
1178
|
}
|
|
1122
1179
|
}
|
|
1123
1180
|
return { output: '', tokens };
|
|
1124
1181
|
}
|
|
1125
1182
|
}
|
|
1126
|
-
catch {
|
|
1183
|
+
catch (err) {
|
|
1184
|
+
// Re-throw SpawnTimeoutError and SpawnAbortedError — only swallow transient poll failures
|
|
1185
|
+
if (err instanceof SpawnTimeoutError ||
|
|
1186
|
+
err instanceof SpawnAbortedError) {
|
|
1187
|
+
throw err;
|
|
1188
|
+
}
|
|
1127
1189
|
// Transient poll failure — keep trying
|
|
1128
1190
|
}
|
|
1129
1191
|
await sleepAsync(this.pollIntervalMs);
|
|
1130
1192
|
}
|
|
1131
|
-
throw new SpawnTimeoutError('Synthesis subprocess timed out after ' + timeoutMs.toString() + 'ms', outputPath);
|
|
1132
1193
|
}
|
|
1133
1194
|
}
|
|
1134
1195
|
|
|
@@ -2626,7 +2687,6 @@ async function runArchitect(node, currentMeta, phaseState, config, executor, wat
|
|
|
2626
2687
|
const architectTask = buildArchitectTask(ctx, currentMeta, config);
|
|
2627
2688
|
const result = await executor.spawn(architectTask, {
|
|
2628
2689
|
thinking: config.thinking,
|
|
2629
|
-
timeout: currentMeta._architectTimeout ?? config.architectTimeout,
|
|
2630
2690
|
label: 'meta-architect',
|
|
2631
2691
|
});
|
|
2632
2692
|
const builderBrief = parseArchitectOutput(result.output);
|
|
@@ -2678,7 +2738,6 @@ async function runBuilder(node, currentMeta, phaseState, config, executor, watch
|
|
|
2678
2738
|
const builderTask = buildBuilderTask(ctx, currentMeta, config);
|
|
2679
2739
|
const result = await executor.spawn(builderTask, {
|
|
2680
2740
|
thinking: config.thinking,
|
|
2681
|
-
timeout: currentMeta._builderTimeout ?? config.builderTimeout,
|
|
2682
2741
|
label: 'meta-builder',
|
|
2683
2742
|
});
|
|
2684
2743
|
const rawOutput = result.output;
|
|
@@ -2773,7 +2832,6 @@ async function runCritic(node, currentMeta, phaseState, config, executor, watche
|
|
|
2773
2832
|
const criticTask = buildCriticTask(ctx, metaForCritic, config);
|
|
2774
2833
|
const result = await executor.spawn(criticTask, {
|
|
2775
2834
|
thinking: config.thinking,
|
|
2776
|
-
timeout: currentMeta._criticTimeout ?? config.criticTimeout,
|
|
2777
2835
|
label: 'meta-critic',
|
|
2778
2836
|
});
|
|
2779
2837
|
const feedback = parseCriticOutput(result.output);
|
|
@@ -3686,12 +3744,6 @@ async function createMeta(ownerPath, options) {
|
|
|
3686
3744
|
metaJson._crossRefs = options.crossRefs;
|
|
3687
3745
|
if (options?.steer !== undefined)
|
|
3688
3746
|
metaJson._steer = options.steer;
|
|
3689
|
-
if (options?.architectTimeout !== undefined)
|
|
3690
|
-
metaJson._architectTimeout = options.architectTimeout;
|
|
3691
|
-
if (options?.builderTimeout !== undefined)
|
|
3692
|
-
metaJson._builderTimeout = options.builderTimeout;
|
|
3693
|
-
if (options?.criticTimeout !== undefined)
|
|
3694
|
-
metaJson._criticTimeout = options.criticTimeout;
|
|
3695
3747
|
const metaJsonPath = join(metaDir, 'meta.json');
|
|
3696
3748
|
await writeFile(metaJsonPath, JSON.stringify(metaJson, null, 2) + '\n');
|
|
3697
3749
|
return { metaDir, _id };
|
|
@@ -3767,9 +3819,6 @@ async function autoSeedPass(rules, watcher, logger) {
|
|
|
3767
3819
|
candidates.set(dir, {
|
|
3768
3820
|
steer: rule.steer,
|
|
3769
3821
|
crossRefs: rule.crossRefs,
|
|
3770
|
-
architectTimeout: rule.architectTimeout,
|
|
3771
|
-
builderTimeout: rule.builderTimeout,
|
|
3772
|
-
criticTimeout: rule.criticTimeout,
|
|
3773
3822
|
});
|
|
3774
3823
|
}
|
|
3775
3824
|
}
|
|
@@ -4339,7 +4388,7 @@ function registerMetasRoutes(app, deps) {
|
|
|
4339
4388
|
/**
|
|
4340
4389
|
* PATCH /metas/:path — update user-settable reserved properties on a meta.
|
|
4341
4390
|
*
|
|
4342
|
-
* Supported fields: _steer, _emphasis, _depth, _crossRefs, _disabled
|
|
4391
|
+
* Supported fields: _steer, _emphasis, _depth, _crossRefs, _disabled.
|
|
4343
4392
|
* Set a field to null to remove it. Unknown keys are rejected.
|
|
4344
4393
|
*
|
|
4345
4394
|
* @module routes/metasUpdate
|
|
@@ -4351,9 +4400,6 @@ const updateBodySchema = z
|
|
|
4351
4400
|
_depth: z.union([z.number(), z.null()]).optional(),
|
|
4352
4401
|
_crossRefs: z.union([z.array(z.string()), z.null()]).optional(),
|
|
4353
4402
|
_disabled: z.union([z.boolean(), z.null()]).optional(),
|
|
4354
|
-
_architectTimeout: z.union([z.number().int().min(30), z.null()]).optional(),
|
|
4355
|
-
_builderTimeout: z.union([z.number().int().min(30), z.null()]).optional(),
|
|
4356
|
-
_criticTimeout: z.union([z.number().int().min(30), z.null()]).optional(),
|
|
4357
4403
|
})
|
|
4358
4404
|
.strict();
|
|
4359
4405
|
function registerMetasUpdateRoute(app, deps) {
|
|
@@ -36,13 +36,32 @@ export declare class GatewayExecutor implements MetaExecutor {
|
|
|
36
36
|
constructor(options?: GatewayExecutorOptions);
|
|
37
37
|
/** Remove a temp output file if it exists. */
|
|
38
38
|
private cleanupOutputFile;
|
|
39
|
+
/** Read and clean up the staging output file. Returns content or undefined if absent. */
|
|
40
|
+
private readStagingFile;
|
|
41
|
+
/** Extract plain text from a message content field, skipping ANNOUNCE_SKIP sentinels. */
|
|
42
|
+
private static extractMessageText;
|
|
43
|
+
/** Check history messages for terminal completion. */
|
|
44
|
+
private static checkHistoryCompletion;
|
|
39
45
|
/** Invoke a gateway tool via the /tools/invoke HTTP endpoint. */
|
|
40
46
|
private invoke;
|
|
41
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Look up session metadata (tokens, completion status) via sessions_list.
|
|
49
|
+
*
|
|
50
|
+
* Detects gateway-side timeout (`status: "timeout"`) and killed sessions
|
|
51
|
+
* (`status: "killed"`) as completed, with a `timedOut` flag to distinguish
|
|
52
|
+
* timeout from normal completion.
|
|
53
|
+
*/
|
|
42
54
|
private getSessionInfo;
|
|
43
55
|
/** Whether this executor has been aborted by the operator. */
|
|
44
56
|
get aborted(): boolean;
|
|
45
57
|
/** Abort the currently running spawn, if any. */
|
|
46
58
|
abort(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Query the gateway's configured subagent run timeout.
|
|
61
|
+
*
|
|
62
|
+
* Returns the value in milliseconds, or `undefined` if the query fails
|
|
63
|
+
* or the value is absent/zero (no timeout configured).
|
|
64
|
+
*/
|
|
65
|
+
private queryGatewayRunTimeout;
|
|
47
66
|
spawn(task: string, options?: MetaSpawnOptions): Promise<MetaSpawnResult>;
|
|
48
67
|
}
|
package/dist/index.js
CHANGED
|
@@ -1271,12 +1271,6 @@ const autoSeedRuleSchema = z.object({
|
|
|
1271
1271
|
crossRefs: z.array(z.string()).optional(),
|
|
1272
1272
|
/** Walk up this many extra parent levels from the matched file's directory. Default 0. */
|
|
1273
1273
|
parentDepth: z.number().int().min(0).optional(),
|
|
1274
|
-
/** Per-category timeout override for the architect phase (seconds, min 30). */
|
|
1275
|
-
architectTimeout: z.number().int().min(30).optional(),
|
|
1276
|
-
/** Per-category timeout override for the builder phase (seconds, min 30). */
|
|
1277
|
-
builderTimeout: z.number().int().min(30).optional(),
|
|
1278
|
-
/** Per-category timeout override for the critic phase (seconds, min 30). */
|
|
1279
|
-
criticTimeout: z.number().int().min(30).optional(),
|
|
1280
1274
|
});
|
|
1281
1275
|
/** Zod schema for jeeves-meta service configuration (superset of MetaConfig). */
|
|
1282
1276
|
const serviceConfigSchema = metaConfigSchema.extend({
|
|
@@ -1459,7 +1453,7 @@ class SpawnTimeoutError extends Error {
|
|
|
1459
1453
|
* @module executor/GatewayExecutor
|
|
1460
1454
|
*/
|
|
1461
1455
|
const DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
1462
|
-
const
|
|
1456
|
+
const DEFAULT_SAFETY_VALVE_MS = 3_600_000; // 1 hour fallback
|
|
1463
1457
|
/**
|
|
1464
1458
|
* MetaExecutor that spawns OpenClaw sessions via the gateway's
|
|
1465
1459
|
* `/tools/invoke` endpoint.
|
|
@@ -1490,6 +1484,42 @@ class GatewayExecutor {
|
|
|
1490
1484
|
/* best-effort cleanup */
|
|
1491
1485
|
}
|
|
1492
1486
|
}
|
|
1487
|
+
/** Read and clean up the staging output file. Returns content or undefined if absent. */
|
|
1488
|
+
readStagingFile(outputPath) {
|
|
1489
|
+
if (!existsSync(outputPath))
|
|
1490
|
+
return undefined;
|
|
1491
|
+
try {
|
|
1492
|
+
return readFileSync(outputPath, 'utf8');
|
|
1493
|
+
}
|
|
1494
|
+
finally {
|
|
1495
|
+
this.cleanupOutputFile(outputPath);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
/** Extract plain text from a message content field, skipping ANNOUNCE_SKIP sentinels. */
|
|
1499
|
+
static extractMessageText(content) {
|
|
1500
|
+
if (!content)
|
|
1501
|
+
return undefined;
|
|
1502
|
+
const text = typeof content === 'string'
|
|
1503
|
+
? content
|
|
1504
|
+
: Array.isArray(content)
|
|
1505
|
+
? content
|
|
1506
|
+
.filter((b) => b.type === 'text' && b.text)
|
|
1507
|
+
.map((b) => b.text)
|
|
1508
|
+
.join('\n')
|
|
1509
|
+
: '';
|
|
1510
|
+
return text && text.trim() !== 'ANNOUNCE_SKIP' ? text : undefined;
|
|
1511
|
+
}
|
|
1512
|
+
/** Check history messages for terminal completion. */
|
|
1513
|
+
static checkHistoryCompletion(messages) {
|
|
1514
|
+
if (messages.length === 0)
|
|
1515
|
+
return { done: false, timedOut: false };
|
|
1516
|
+
const last = messages[messages.length - 1];
|
|
1517
|
+
if (last.role !== 'assistant' || !last.stopReason)
|
|
1518
|
+
return { done: false, timedOut: false };
|
|
1519
|
+
if (last.stopReason === 'toolUse' || last.stopReason === 'error')
|
|
1520
|
+
return { done: false, timedOut: false };
|
|
1521
|
+
return { done: true, timedOut: last.stopReason === 'timeout' };
|
|
1522
|
+
}
|
|
1493
1523
|
/** Invoke a gateway tool via the /tools/invoke HTTP endpoint. */
|
|
1494
1524
|
async invoke(tool, args, sessionKey) {
|
|
1495
1525
|
const headers = {
|
|
@@ -1516,7 +1546,13 @@ class GatewayExecutor {
|
|
|
1516
1546
|
}
|
|
1517
1547
|
return data;
|
|
1518
1548
|
}
|
|
1519
|
-
/**
|
|
1549
|
+
/**
|
|
1550
|
+
* Look up session metadata (tokens, completion status) via sessions_list.
|
|
1551
|
+
*
|
|
1552
|
+
* Detects gateway-side timeout (`status: "timeout"`) and killed sessions
|
|
1553
|
+
* (`status: "killed"`) as completed, with a `timedOut` flag to distinguish
|
|
1554
|
+
* timeout from normal completion.
|
|
1555
|
+
*/
|
|
1520
1556
|
async getSessionInfo(sessionKey) {
|
|
1521
1557
|
try {
|
|
1522
1558
|
const result = await this.invoke('sessions_list', {
|
|
@@ -1532,13 +1568,18 @@ class GatewayExecutor {
|
|
|
1532
1568
|
// With limit=200 this is reliable; a false positive here only
|
|
1533
1569
|
// means we read the output file slightly early (still correct
|
|
1534
1570
|
// if the file exists).
|
|
1535
|
-
return { completed: true };
|
|
1571
|
+
return { completed: true, timedOut: false };
|
|
1536
1572
|
}
|
|
1537
|
-
const
|
|
1538
|
-
|
|
1573
|
+
const status = match.status;
|
|
1574
|
+
const done = status === 'completed' ||
|
|
1575
|
+
status === 'done' ||
|
|
1576
|
+
status === 'timeout' ||
|
|
1577
|
+
status === 'killed';
|
|
1578
|
+
const timedOut = status === 'timeout';
|
|
1579
|
+
return { tokens: match.totalTokens, completed: done, timedOut };
|
|
1539
1580
|
}
|
|
1540
1581
|
catch {
|
|
1541
|
-
return { completed: false };
|
|
1582
|
+
return { completed: false, timedOut: false };
|
|
1542
1583
|
}
|
|
1543
1584
|
}
|
|
1544
1585
|
/** Whether this executor has been aborted by the operator. */
|
|
@@ -1549,12 +1590,38 @@ class GatewayExecutor {
|
|
|
1549
1590
|
abort() {
|
|
1550
1591
|
this.controller.abort();
|
|
1551
1592
|
}
|
|
1593
|
+
/**
|
|
1594
|
+
* Query the gateway's configured subagent run timeout.
|
|
1595
|
+
*
|
|
1596
|
+
* Returns the value in milliseconds, or `undefined` if the query fails
|
|
1597
|
+
* or the value is absent/zero (no timeout configured).
|
|
1598
|
+
*/
|
|
1599
|
+
async queryGatewayRunTimeout() {
|
|
1600
|
+
try {
|
|
1601
|
+
const result = await this.invoke('session_status', {});
|
|
1602
|
+
const details = (result.result?.details ?? result.result ?? {});
|
|
1603
|
+
const runTimeoutSeconds = details.runTimeoutSeconds ??
|
|
1604
|
+
details.timeout;
|
|
1605
|
+
if (typeof runTimeoutSeconds === 'number' && runTimeoutSeconds > 0) {
|
|
1606
|
+
return runTimeoutSeconds * 1000;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
catch {
|
|
1610
|
+
// Gateway unreachable or field not exposed — fall back to default
|
|
1611
|
+
}
|
|
1612
|
+
return undefined;
|
|
1613
|
+
}
|
|
1552
1614
|
async spawn(task, options) {
|
|
1553
1615
|
// Fresh controller for each spawn call
|
|
1554
1616
|
this.controller = new AbortController();
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1617
|
+
// Safety-valve deadline: gateway's runTimeoutSeconds + 60s buffer,
|
|
1618
|
+
// defaulting to 1 hour if the gateway value is 0/absent/query fails.
|
|
1619
|
+
// This is a circuit breaker, not a timeout mechanism.
|
|
1620
|
+
const gatewayTimeoutMs = await this.queryGatewayRunTimeout();
|
|
1621
|
+
const safetyValveMs = gatewayTimeoutMs
|
|
1622
|
+
? gatewayTimeoutMs + 60_000
|
|
1623
|
+
: DEFAULT_SAFETY_VALVE_MS;
|
|
1624
|
+
const safetyDeadline = Date.now() + safetyValveMs;
|
|
1558
1625
|
// Ensure workspace dir exists
|
|
1559
1626
|
if (!existsSync(this.workspaceDir)) {
|
|
1560
1627
|
mkdirSync(this.workspaceDir, { recursive: true });
|
|
@@ -1576,7 +1643,6 @@ class GatewayExecutor {
|
|
|
1576
1643
|
const spawnResult = await this.invoke('sessions_spawn', {
|
|
1577
1644
|
task: taskWithOutput,
|
|
1578
1645
|
label,
|
|
1579
|
-
runTimeoutSeconds: timeoutSeconds,
|
|
1580
1646
|
...(options?.thinking ? { thinking: options.thinking } : {}),
|
|
1581
1647
|
...(options?.model ? { model: options.model } : {}),
|
|
1582
1648
|
});
|
|
@@ -1588,9 +1654,18 @@ class GatewayExecutor {
|
|
|
1588
1654
|
throw new Error('Gateway sessions_spawn returned no sessionKey: ' +
|
|
1589
1655
|
JSON.stringify(spawnResult));
|
|
1590
1656
|
}
|
|
1591
|
-
// Step 2: Poll for completion
|
|
1657
|
+
// Step 2: Poll for completion — gateway owns the subagent lifecycle.
|
|
1658
|
+
// Loop exits via: (a) completion detection, (b) abort signal,
|
|
1659
|
+
// (c) gateway-side timeout detection, or (d) safety-valve circuit breaker.
|
|
1592
1660
|
await sleepAsync(3000);
|
|
1593
|
-
while (
|
|
1661
|
+
while (true) {
|
|
1662
|
+
// Safety-valve circuit breaker
|
|
1663
|
+
if (Date.now() >= safetyDeadline) {
|
|
1664
|
+
this.cleanupOutputFile(outputPath);
|
|
1665
|
+
throw new SpawnTimeoutError('Safety-valve deadline exceeded (' +
|
|
1666
|
+
safetyValveMs.toString() +
|
|
1667
|
+
'ms) — gateway timeout may be misconfigured', outputPath);
|
|
1668
|
+
}
|
|
1594
1669
|
// Check for abort before each poll iteration
|
|
1595
1670
|
if (this.controller.signal.aborted) {
|
|
1596
1671
|
this.cleanupOutputFile(outputPath);
|
|
@@ -1607,62 +1682,48 @@ class GatewayExecutor {
|
|
|
1607
1682
|
[];
|
|
1608
1683
|
const msgArray = messages;
|
|
1609
1684
|
// Check 1: terminal stop reason in history
|
|
1610
|
-
|
|
1611
|
-
if (msgArray.length > 0) {
|
|
1612
|
-
const lastMsg = msgArray[msgArray.length - 1];
|
|
1613
|
-
if (lastMsg.role === 'assistant' &&
|
|
1614
|
-
lastMsg.stopReason &&
|
|
1615
|
-
lastMsg.stopReason !== 'toolUse' &&
|
|
1616
|
-
lastMsg.stopReason !== 'error') {
|
|
1617
|
-
historyDone = true;
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1685
|
+
const { done: historyDone, timedOut: historyTimedOut } = GatewayExecutor.checkHistoryCompletion(msgArray);
|
|
1620
1686
|
// Check 2: session completion status via sessions_list
|
|
1621
1687
|
const sessionInfo = await this.getSessionInfo(sessionKey);
|
|
1688
|
+
const timedOut = sessionInfo.timedOut || historyTimedOut;
|
|
1622
1689
|
if (historyDone || sessionInfo.completed) {
|
|
1623
1690
|
const tokens = sessionInfo.tokens;
|
|
1624
|
-
//
|
|
1625
|
-
if (
|
|
1626
|
-
|
|
1627
|
-
|
|
1691
|
+
// Gateway-side timeout detected — check staging file for recovery
|
|
1692
|
+
if (timedOut) {
|
|
1693
|
+
const output = this.readStagingFile(outputPath);
|
|
1694
|
+
if (output !== undefined)
|
|
1628
1695
|
return { output, tokens };
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
try {
|
|
1632
|
-
unlinkSync(outputPath);
|
|
1633
|
-
}
|
|
1634
|
-
catch {
|
|
1635
|
-
/* cleanup best-effort */
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1696
|
+
// No output or partial output — throw for _state recovery (§3.16.6)
|
|
1697
|
+
throw new SpawnTimeoutError('Gateway-side timeout detected (session status: timeout)', outputPath);
|
|
1638
1698
|
}
|
|
1699
|
+
// Normal completion — read output from file
|
|
1700
|
+
const output = this.readStagingFile(outputPath);
|
|
1701
|
+
if (output !== undefined)
|
|
1702
|
+
return { output, tokens };
|
|
1639
1703
|
// Fallback: extract from message content if file wasn't written.
|
|
1640
1704
|
// Skip ANNOUNCE_SKIP sentinel messages — the real output is in
|
|
1641
1705
|
// a preceding assistant message (the file write).
|
|
1642
1706
|
for (let i = msgArray.length - 1; i >= 0; i--) {
|
|
1643
1707
|
const msg = msgArray[i];
|
|
1644
|
-
if (msg.role === 'assistant'
|
|
1645
|
-
const text =
|
|
1646
|
-
|
|
1647
|
-
: Array.isArray(msg.content)
|
|
1648
|
-
? msg.content
|
|
1649
|
-
.filter((b) => b.type === 'text' && b.text)
|
|
1650
|
-
.map((b) => b.text)
|
|
1651
|
-
.join('\n')
|
|
1652
|
-
: '';
|
|
1653
|
-
if (text && text.trim() !== 'ANNOUNCE_SKIP')
|
|
1708
|
+
if (msg.role === 'assistant') {
|
|
1709
|
+
const text = GatewayExecutor.extractMessageText(msg.content);
|
|
1710
|
+
if (text !== undefined)
|
|
1654
1711
|
return { output: text, tokens };
|
|
1655
1712
|
}
|
|
1656
1713
|
}
|
|
1657
1714
|
return { output: '', tokens };
|
|
1658
1715
|
}
|
|
1659
1716
|
}
|
|
1660
|
-
catch {
|
|
1717
|
+
catch (err) {
|
|
1718
|
+
// Re-throw SpawnTimeoutError and SpawnAbortedError — only swallow transient poll failures
|
|
1719
|
+
if (err instanceof SpawnTimeoutError ||
|
|
1720
|
+
err instanceof SpawnAbortedError) {
|
|
1721
|
+
throw err;
|
|
1722
|
+
}
|
|
1661
1723
|
// Transient poll failure — keep trying
|
|
1662
1724
|
}
|
|
1663
1725
|
await sleepAsync(this.pollIntervalMs);
|
|
1664
1726
|
}
|
|
1665
|
-
throw new SpawnTimeoutError('Synthesis subprocess timed out after ' + timeoutMs.toString() + 'ms', outputPath);
|
|
1666
1727
|
}
|
|
1667
1728
|
}
|
|
1668
1729
|
|
|
@@ -2884,7 +2945,6 @@ async function runArchitect(node, currentMeta, phaseState, config, executor, wat
|
|
|
2884
2945
|
const architectTask = buildArchitectTask(ctx, currentMeta, config);
|
|
2885
2946
|
const result = await executor.spawn(architectTask, {
|
|
2886
2947
|
thinking: config.thinking,
|
|
2887
|
-
timeout: currentMeta._architectTimeout ?? config.architectTimeout,
|
|
2888
2948
|
label: 'meta-architect',
|
|
2889
2949
|
});
|
|
2890
2950
|
const builderBrief = parseArchitectOutput(result.output);
|
|
@@ -2936,7 +2996,6 @@ async function runBuilder(node, currentMeta, phaseState, config, executor, watch
|
|
|
2936
2996
|
const builderTask = buildBuilderTask(ctx, currentMeta, config);
|
|
2937
2997
|
const result = await executor.spawn(builderTask, {
|
|
2938
2998
|
thinking: config.thinking,
|
|
2939
|
-
timeout: currentMeta._builderTimeout ?? config.builderTimeout,
|
|
2940
2999
|
label: 'meta-builder',
|
|
2941
3000
|
});
|
|
2942
3001
|
const rawOutput = result.output;
|
|
@@ -3031,7 +3090,6 @@ async function runCritic(node, currentMeta, phaseState, config, executor, watche
|
|
|
3031
3090
|
const criticTask = buildCriticTask(ctx, metaForCritic, config);
|
|
3032
3091
|
const result = await executor.spawn(criticTask, {
|
|
3033
3092
|
thinking: config.thinking,
|
|
3034
|
-
timeout: currentMeta._criticTimeout ?? config.criticTimeout,
|
|
3035
3093
|
label: 'meta-critic',
|
|
3036
3094
|
});
|
|
3037
3095
|
const feedback = parseCriticOutput(result.output);
|
|
@@ -3944,12 +4002,6 @@ async function createMeta(ownerPath, options) {
|
|
|
3944
4002
|
metaJson._crossRefs = options.crossRefs;
|
|
3945
4003
|
if (options?.steer !== undefined)
|
|
3946
4004
|
metaJson._steer = options.steer;
|
|
3947
|
-
if (options?.architectTimeout !== undefined)
|
|
3948
|
-
metaJson._architectTimeout = options.architectTimeout;
|
|
3949
|
-
if (options?.builderTimeout !== undefined)
|
|
3950
|
-
metaJson._builderTimeout = options.builderTimeout;
|
|
3951
|
-
if (options?.criticTimeout !== undefined)
|
|
3952
|
-
metaJson._criticTimeout = options.criticTimeout;
|
|
3953
4005
|
const metaJsonPath = join(metaDir, 'meta.json');
|
|
3954
4006
|
await writeFile(metaJsonPath, JSON.stringify(metaJson, null, 2) + '\n');
|
|
3955
4007
|
return { metaDir, _id };
|
|
@@ -4025,9 +4077,6 @@ async function autoSeedPass(rules, watcher, logger) {
|
|
|
4025
4077
|
candidates.set(dir, {
|
|
4026
4078
|
steer: rule.steer,
|
|
4027
4079
|
crossRefs: rule.crossRefs,
|
|
4028
|
-
architectTimeout: rule.architectTimeout,
|
|
4029
|
-
builderTimeout: rule.builderTimeout,
|
|
4030
|
-
criticTimeout: rule.criticTimeout,
|
|
4031
4080
|
});
|
|
4032
4081
|
}
|
|
4033
4082
|
}
|
|
@@ -4597,7 +4646,7 @@ function registerMetasRoutes(app, deps) {
|
|
|
4597
4646
|
/**
|
|
4598
4647
|
* PATCH /metas/:path — update user-settable reserved properties on a meta.
|
|
4599
4648
|
*
|
|
4600
|
-
* Supported fields: _steer, _emphasis, _depth, _crossRefs, _disabled
|
|
4649
|
+
* Supported fields: _steer, _emphasis, _depth, _crossRefs, _disabled.
|
|
4601
4650
|
* Set a field to null to remove it. Unknown keys are rejected.
|
|
4602
4651
|
*
|
|
4603
4652
|
* @module routes/metasUpdate
|
|
@@ -4609,9 +4658,6 @@ const updateBodySchema = z
|
|
|
4609
4658
|
_depth: z.union([z.number(), z.null()]).optional(),
|
|
4610
4659
|
_crossRefs: z.union([z.array(z.string()), z.null()]).optional(),
|
|
4611
4660
|
_disabled: z.union([z.boolean(), z.null()]).optional(),
|
|
4612
|
-
_architectTimeout: z.union([z.number().int().min(30), z.null()]).optional(),
|
|
4613
|
-
_builderTimeout: z.union([z.number().int().min(30), z.null()]).optional(),
|
|
4614
|
-
_criticTimeout: z.union([z.number().int().min(30), z.null()]).optional(),
|
|
4615
4661
|
})
|
|
4616
4662
|
.strict();
|
|
4617
4663
|
function registerMetasUpdateRoute(app, deps) {
|
|
@@ -5817,12 +5863,6 @@ const metaJsonSchema = z
|
|
|
5817
5863
|
_error: metaErrorSchema.optional(),
|
|
5818
5864
|
/** When true, this meta is skipped during staleness scheduling. Manual trigger still works. */
|
|
5819
5865
|
_disabled: z.boolean().optional(),
|
|
5820
|
-
/** Per-entity timeout override for the architect phase (seconds, min 30). */
|
|
5821
|
-
_architectTimeout: z.number().int().min(30).optional(),
|
|
5822
|
-
/** Per-entity timeout override for the builder phase (seconds, min 30). */
|
|
5823
|
-
_builderTimeout: z.number().int().min(30).optional(),
|
|
5824
|
-
/** Per-entity timeout override for the critic phase (seconds, min 30). */
|
|
5825
|
-
_criticTimeout: z.number().int().min(30).optional(),
|
|
5826
5866
|
/**
|
|
5827
5867
|
* SHA-256 hash of ancestor _builder text at last synthesis.
|
|
5828
5868
|
* Observability only — no invalidation cascade.
|
|
@@ -7,8 +7,6 @@
|
|
|
7
7
|
export interface MetaSpawnOptions {
|
|
8
8
|
/** Model override for this subprocess. */
|
|
9
9
|
model?: string;
|
|
10
|
-
/** Timeout in seconds. */
|
|
11
|
-
timeout?: number;
|
|
12
10
|
/** Label for the spawned session. */
|
|
13
11
|
label?: string;
|
|
14
12
|
/** Thinking level (e.g. "low", "medium", "high"). */
|
|
@@ -128,8 +128,6 @@ The Builder should:
|
|
|
128
128
|
4. Merge new findings with previous `_content` (carried in context)
|
|
129
129
|
|
|
130
130
|
If the scope is small enough to process in one pass, omit chunking instructions.
|
|
131
|
-
The Builder has a timeout of \{{config.builderTimeout}} seconds.
|
|
132
|
-
|
|
133
131
|
### 8. Output Structure
|
|
134
132
|
|
|
135
133
|
Define non-underscore fields for structured data and the _content narrative
|
|
@@ -148,7 +146,6 @@ Quote the specific issue and state what to do differently.
|
|
|
148
146
|
Your task brief will be compiled as a Handlebars template before the Builder
|
|
149
147
|
receives it. You can use these variables to write adaptive instructions:
|
|
150
148
|
|
|
151
|
-
- `\{{config.builderTimeout}}` — Builder timeout in seconds
|
|
152
149
|
- `\{{config.maxLines}}` — Maximum _content lines
|
|
153
150
|
- `\{{config.architectEvery}}` — Cycles between architect refreshes
|
|
154
151
|
- `\{{config.maxArchive}}` — Archive snapshots retained
|
|
@@ -159,7 +156,7 @@ receives it. You can use these variables to write adaptive instructions:
|
|
|
159
156
|
- `\{{meta._depth}}` — Scheduling depth
|
|
160
157
|
- `\{{meta._emphasis}}` — Scheduling emphasis
|
|
161
158
|
|
|
162
|
-
Example: "Process files in chunks of 50.
|
|
159
|
+
Example: "Process files in chunks of 50. Limit output to \{{config.maxLines}} lines."
|
|
163
160
|
|
|
164
161
|
## Constraints
|
|
165
162
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PATCH /metas/:path — update user-settable reserved properties on a meta.
|
|
3
3
|
*
|
|
4
|
-
* Supported fields: _steer, _emphasis, _depth, _crossRefs, _disabled
|
|
4
|
+
* Supported fields: _steer, _emphasis, _depth, _crossRefs, _disabled.
|
|
5
5
|
* Set a field to null to remove it. Unknown keys are rejected.
|
|
6
6
|
*
|
|
7
7
|
* @module routes/metasUpdate
|
package/dist/schema/config.d.ts
CHANGED
|
@@ -14,9 +14,6 @@ declare const autoSeedRuleSchema: z.ZodObject<{
|
|
|
14
14
|
steer: z.ZodOptional<z.ZodString>;
|
|
15
15
|
crossRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
16
16
|
parentDepth: z.ZodOptional<z.ZodNumber>;
|
|
17
|
-
architectTimeout: z.ZodOptional<z.ZodNumber>;
|
|
18
|
-
builderTimeout: z.ZodOptional<z.ZodNumber>;
|
|
19
|
-
criticTimeout: z.ZodOptional<z.ZodNumber>;
|
|
20
17
|
}, z.core.$strip>;
|
|
21
18
|
/** Inferred type for an auto-seed rule. */
|
|
22
19
|
export type AutoSeedRule = z.infer<typeof autoSeedRuleSchema>;
|
|
@@ -29,9 +26,6 @@ export declare const serviceConfigSchema: z.ZodObject<{
|
|
|
29
26
|
depthWeight: z.ZodDefault<z.ZodNumber>;
|
|
30
27
|
maxArchive: z.ZodDefault<z.ZodNumber>;
|
|
31
28
|
maxLines: z.ZodDefault<z.ZodNumber>;
|
|
32
|
-
architectTimeout: z.ZodDefault<z.ZodNumber>;
|
|
33
|
-
builderTimeout: z.ZodDefault<z.ZodNumber>;
|
|
34
|
-
criticTimeout: z.ZodDefault<z.ZodNumber>;
|
|
35
29
|
thinking: z.ZodDefault<z.ZodString>;
|
|
36
30
|
defaultArchitect: z.ZodOptional<z.ZodString>;
|
|
37
31
|
defaultCritic: z.ZodOptional<z.ZodString>;
|
|
@@ -54,9 +48,6 @@ export declare const serviceConfigSchema: z.ZodObject<{
|
|
|
54
48
|
steer: z.ZodOptional<z.ZodString>;
|
|
55
49
|
crossRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
56
50
|
parentDepth: z.ZodOptional<z.ZodNumber>;
|
|
57
|
-
architectTimeout: z.ZodOptional<z.ZodNumber>;
|
|
58
|
-
builderTimeout: z.ZodOptional<z.ZodNumber>;
|
|
59
|
-
criticTimeout: z.ZodOptional<z.ZodNumber>;
|
|
60
51
|
}, z.core.$strip>>>>;
|
|
61
52
|
}, z.core.$strip>;
|
|
62
53
|
/** Inferred type for service configuration. */
|
package/dist/schema/meta.d.ts
CHANGED
|
@@ -43,9 +43,6 @@ export declare const metaJsonSchema: z.ZodObject<{
|
|
|
43
43
|
message: z.ZodString;
|
|
44
44
|
}, z.core.$strip>>;
|
|
45
45
|
_disabled: z.ZodOptional<z.ZodBoolean>;
|
|
46
|
-
_architectTimeout: z.ZodOptional<z.ZodNumber>;
|
|
47
|
-
_builderTimeout: z.ZodOptional<z.ZodNumber>;
|
|
48
|
-
_criticTimeout: z.ZodOptional<z.ZodNumber>;
|
|
49
46
|
_ancestorBuilderHash: z.ZodOptional<z.ZodString>;
|
|
50
47
|
_phaseState: z.ZodOptional<z.ZodObject<{
|
|
51
48
|
architect: z.ZodEnum<{
|
|
@@ -11,12 +11,6 @@ export interface CreateMetaOptions {
|
|
|
11
11
|
crossRefs?: string[];
|
|
12
12
|
/** Steering prompt for the meta. */
|
|
13
13
|
steer?: string;
|
|
14
|
-
/** Per-entity timeout override for the architect phase (seconds). */
|
|
15
|
-
architectTimeout?: number;
|
|
16
|
-
/** Per-entity timeout override for the builder phase (seconds). */
|
|
17
|
-
builderTimeout?: number;
|
|
18
|
-
/** Per-entity timeout override for the critic phase (seconds). */
|
|
19
|
-
criticTimeout?: number;
|
|
20
14
|
}
|
|
21
15
|
/** Result of creating a new meta. */
|
|
22
16
|
export interface CreateMetaResult {
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@karmaniverous/jeeves": "^0.5.12",
|
|
11
|
-
"@karmaniverous/jeeves-meta-core": "^0.
|
|
11
|
+
"@karmaniverous/jeeves-meta-core": "^0.2.0",
|
|
12
12
|
"commander": "^14",
|
|
13
13
|
"croner": "^10",
|
|
14
14
|
"fastify": "^5.8",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"url": "git+https://github.com/karmaniverous/jeeves-meta.git"
|
|
99
99
|
},
|
|
100
100
|
"scripts": {
|
|
101
|
-
"build": "rimraf dist && cross-env NO_COLOR=1 rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
|
|
101
|
+
"build": "rimraf dist && cross-env NO_COLOR=1 rollup --config rollup.config.ts --configPlugin \"@rollup/plugin-typescript={outputToFilesystem:true}\"",
|
|
102
102
|
"changelog": "git-cliff -o CHANGELOG.md",
|
|
103
103
|
"knip": "knip",
|
|
104
104
|
"lint": "eslint .",
|
|
@@ -110,5 +110,5 @@
|
|
|
110
110
|
},
|
|
111
111
|
"type": "module",
|
|
112
112
|
"types": "dist/index.d.ts",
|
|
113
|
-
"version": "0.
|
|
113
|
+
"version": "0.16.0"
|
|
114
114
|
}
|