@geekbeer/minion 3.29.4 → 3.32.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/core/lib/dag-step-poller.js +30 -6
- package/core/llm-plugins/claude/stream.js +1 -1
- package/core/routes/skills.js +6 -0
- package/linux/minion-cli.sh +18 -3
- package/linux/routes/chat.js +31 -1
- package/linux/workflow-runner.js +32 -1
- package/package.json +1 -1
- package/win/minion-cli.ps1 +15 -1
- package/win/routes/chat.js +31 -1
- package/win/workflow-runner.js +32 -1
- package/win/wsl-session-server.js +1 -1
|
@@ -126,6 +126,8 @@ async function executeSkillNode(node) {
|
|
|
126
126
|
input_data,
|
|
127
127
|
revision_feedback,
|
|
128
128
|
review_history,
|
|
129
|
+
input_contracts,
|
|
130
|
+
output_contracts,
|
|
129
131
|
} = node
|
|
130
132
|
|
|
131
133
|
console.log(
|
|
@@ -174,16 +176,18 @@ async function executeSkillNode(node) {
|
|
|
174
176
|
}
|
|
175
177
|
}
|
|
176
178
|
|
|
177
|
-
// 4. Run the skill with input_data context
|
|
179
|
+
// 4. Run the skill with input_data and contract context
|
|
178
180
|
const runPayload = {
|
|
179
181
|
skill_name: skillName,
|
|
180
182
|
execution_id,
|
|
181
183
|
workflow_name: dag_workflow_name,
|
|
182
184
|
role: assigned_role,
|
|
183
|
-
// DAG-specific: inject input_data as context
|
|
185
|
+
// DAG-specific: inject input_data and contracts as context
|
|
184
186
|
dag_node_id: node_id,
|
|
185
187
|
dag_input_data: input_data,
|
|
186
188
|
dag_node_execution_id: node_execution_id,
|
|
189
|
+
dag_input_contracts: input_contracts || null,
|
|
190
|
+
dag_output_contracts: output_contracts || null,
|
|
187
191
|
}
|
|
188
192
|
if (revision_feedback) {
|
|
189
193
|
runPayload.revision_feedback = revision_feedback
|
|
@@ -251,6 +255,7 @@ async function executeTransformNode(node) {
|
|
|
251
255
|
assigned_role,
|
|
252
256
|
input_data,
|
|
253
257
|
transform_instruction,
|
|
258
|
+
output_contracts,
|
|
254
259
|
} = node
|
|
255
260
|
|
|
256
261
|
console.log(
|
|
@@ -285,7 +290,7 @@ async function executeTransformNode(node) {
|
|
|
285
290
|
|
|
286
291
|
// 2. Create ephemeral skill from transform_instruction.
|
|
287
292
|
// Write to every active plugin's skill dir so any Primary can find it.
|
|
288
|
-
const skillContent = buildTransformSkillContent(transform_instruction, input_data)
|
|
293
|
+
const skillContent = buildTransformSkillContent(transform_instruction, input_data, output_contracts)
|
|
289
294
|
for (const dir of ephemeralSkillDirs) {
|
|
290
295
|
await fs.mkdir(dir, { recursive: true })
|
|
291
296
|
await fs.writeFile(path.join(dir, 'SKILL.md'), skillContent, 'utf-8')
|
|
@@ -353,8 +358,8 @@ async function executeTransformNode(node) {
|
|
|
353
358
|
/**
|
|
354
359
|
* Build SKILL.md content for a transform node's ephemeral skill.
|
|
355
360
|
*/
|
|
356
|
-
function buildTransformSkillContent(instruction, inputData) {
|
|
357
|
-
|
|
361
|
+
function buildTransformSkillContent(instruction, inputData, outputContracts) {
|
|
362
|
+
const lines = [
|
|
358
363
|
'---',
|
|
359
364
|
'name: dag-transform',
|
|
360
365
|
'description: DAG Transform Node',
|
|
@@ -369,12 +374,31 @@ function buildTransformSkillContent(instruction, inputData) {
|
|
|
369
374
|
'',
|
|
370
375
|
'## Transform Instruction',
|
|
371
376
|
instruction,
|
|
377
|
+
]
|
|
378
|
+
|
|
379
|
+
if (outputContracts && outputContracts.length > 0) {
|
|
380
|
+
lines.push('', '## Output Contract')
|
|
381
|
+
lines.push('Your output MUST conform to the following contract(s):')
|
|
382
|
+
for (const oc of outputContracts) {
|
|
383
|
+
lines.push(`### ${oc.contract_name}`)
|
|
384
|
+
lines.push(oc.contract.description || '')
|
|
385
|
+
lines.push('| Field | Type | Required | Description |')
|
|
386
|
+
lines.push('|-------|------|----------|-------------|')
|
|
387
|
+
for (const f of oc.contract.fields || []) {
|
|
388
|
+
lines.push(`| ${f.key} | ${f.type} | ${f.required ? 'Yes' : 'No'} | ${f.description || ''} |`)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
lines.push(
|
|
372
394
|
'',
|
|
373
395
|
'## Task',
|
|
374
396
|
'Apply the transform instruction to the input data above.',
|
|
375
397
|
'Output the result as a JSON object in an "## Output Data" section with a json code block.',
|
|
376
398
|
'Do NOT output anything other than the Output Data section.',
|
|
377
|
-
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
return lines.join('\n')
|
|
378
402
|
}
|
|
379
403
|
|
|
380
404
|
/**
|
|
@@ -35,7 +35,7 @@ function streamClaude(input, onEvent, ctx = {}) {
|
|
|
35
35
|
const child = spawn(bin, args, {
|
|
36
36
|
cwd: config.HOME_DIR,
|
|
37
37
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
38
|
-
timeout: input.timeoutMs ||
|
|
38
|
+
timeout: input.timeoutMs || 3600000, // 60 min default
|
|
39
39
|
env: {
|
|
40
40
|
...process.env,
|
|
41
41
|
HOME: config.HOME_DIR,
|
package/core/routes/skills.js
CHANGED
|
@@ -344,6 +344,9 @@ async function skillRoutes(fastify, opts) {
|
|
|
344
344
|
revision_feedback,
|
|
345
345
|
review_history,
|
|
346
346
|
dag_node_execution_id,
|
|
347
|
+
dag_input_data,
|
|
348
|
+
dag_input_contracts,
|
|
349
|
+
dag_output_contracts,
|
|
347
350
|
} = request.body || {}
|
|
348
351
|
|
|
349
352
|
if (!skill_name) {
|
|
@@ -393,6 +396,9 @@ async function skillRoutes(fastify, opts) {
|
|
|
393
396
|
if (role) runOptions.role = role
|
|
394
397
|
if (revision_feedback) runOptions.revisionFeedback = revision_feedback
|
|
395
398
|
if (review_history && review_history.length > 0) runOptions.reviewHistory = review_history
|
|
399
|
+
if (dag_input_data) runOptions.dagInputData = dag_input_data
|
|
400
|
+
if (dag_input_contracts) runOptions.dagInputContracts = dag_input_contracts
|
|
401
|
+
if (dag_output_contracts) runOptions.dagOutputContracts = dag_output_contracts
|
|
396
402
|
|
|
397
403
|
// Run asynchronously — respond immediately
|
|
398
404
|
const executionPromise = (async () => {
|
package/linux/minion-cli.sh
CHANGED
|
@@ -1096,13 +1096,28 @@ SUPEOF
|
|
|
1096
1096
|
HOSTNAME_VAL=$(hostname 2>/dev/null || echo "")
|
|
1097
1097
|
LAN_IP=$(detect_lan_ip)
|
|
1098
1098
|
|
|
1099
|
+
# Collect machine specs
|
|
1100
|
+
local CPU_MODEL CPU_CORES MEMORY_MB DISK_GB OS_INFO ARCH_INFO
|
|
1101
|
+
CPU_MODEL=$(grep -m1 'model name' /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^ //' || echo "unknown")
|
|
1102
|
+
CPU_CORES=$(nproc 2>/dev/null || grep -c '^processor' /proc/cpuinfo 2>/dev/null || echo "0")
|
|
1103
|
+
MEMORY_MB=$(awk '/MemTotal/ {printf "%.0f", $2/1024}' /proc/meminfo 2>/dev/null || echo "0")
|
|
1104
|
+
DISK_GB=$(df -BG / 2>/dev/null | awk 'NR==2 {gsub("G",""); print $2}' || echo "0")
|
|
1105
|
+
OS_INFO=$(. /etc/os-release 2>/dev/null && echo "${PRETTY_NAME}" || uname -s 2>/dev/null || echo "unknown")
|
|
1106
|
+
ARCH_INFO=$(uname -m 2>/dev/null || echo "unknown")
|
|
1107
|
+
|
|
1108
|
+
local MACHINE_SPECS
|
|
1109
|
+
MACHINE_SPECS=$(cat <<SPECEOF
|
|
1110
|
+
{"cpu_model":"${CPU_MODEL}","cpu_cores":${CPU_CORES},"memory_mb":${MEMORY_MB},"disk_gb":${DISK_GB},"os":"${OS_INFO}","arch":"${ARCH_INFO}"}
|
|
1111
|
+
SPECEOF
|
|
1112
|
+
)
|
|
1113
|
+
|
|
1099
1114
|
local BODY
|
|
1100
1115
|
if [ -f /.dockerenv ]; then
|
|
1101
|
-
BODY="{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}"
|
|
1116
|
+
BODY="{\"internal_ip_address\":\"${HOSTNAME_VAL}\",\"machine_specs\":${MACHINE_SPECS}}"
|
|
1102
1117
|
elif [ -n "$LAN_IP" ]; then
|
|
1103
|
-
BODY="{\"internal_ip_address\":\"${LAN_IP}\",\"ip_address\":\"${LAN_IP}\"}"
|
|
1118
|
+
BODY="{\"internal_ip_address\":\"${LAN_IP}\",\"ip_address\":\"${LAN_IP}\",\"machine_specs\":${MACHINE_SPECS}}"
|
|
1104
1119
|
else
|
|
1105
|
-
BODY="{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}"
|
|
1120
|
+
BODY="{\"internal_ip_address\":\"${HOSTNAME_VAL}\",\"machine_specs\":${MACHINE_SPECS}}"
|
|
1106
1121
|
fi
|
|
1107
1122
|
|
|
1108
1123
|
NOTIFY_RESPONSE=$(curl -sfL -X POST "${HQ_URL}/api/minion/setup-complete" \
|
package/linux/routes/chat.js
CHANGED
|
@@ -353,6 +353,36 @@ async function buildContextPrefix(message, context, sessionId, workspaceId) {
|
|
|
353
353
|
)
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
+
// Task planning guidance — instruct Claude to create TODOs before starting work
|
|
357
|
+
if (!sessionId) {
|
|
358
|
+
const port = require('../../core/config').config.AGENT_PORT
|
|
359
|
+
parts.push(
|
|
360
|
+
'[作業手順 — 必ず守ること]',
|
|
361
|
+
'依頼された作業に着手する**前に**、以下の手順を踏むこと:',
|
|
362
|
+
'',
|
|
363
|
+
'1. 依頼内容を分析し、必要なステップをToDoとして登録する',
|
|
364
|
+
'2. 各ステップを完了するたびに、ToDoのstatusを `done` に更新する',
|
|
365
|
+
'3. すべてのToDoが完了したら、ユーザーに完了報告する',
|
|
366
|
+
'',
|
|
367
|
+
'ToDo APIの使い方:',
|
|
368
|
+
'```bash',
|
|
369
|
+
'# ToDo作成(session_idは後でセッションIDが判明してから設定)',
|
|
370
|
+
`curl -X POST http://localhost:${port}/api/todos \\`,
|
|
371
|
+
' -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \\',
|
|
372
|
+
' -d \'{"title": "ステップの説明", "session_id": "SESSION_ID", "priority": "normal"}\'',
|
|
373
|
+
'',
|
|
374
|
+
'# ToDo完了',
|
|
375
|
+
`curl -X PUT http://localhost:${port}/api/todos/{id} \\`,
|
|
376
|
+
' -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \\',
|
|
377
|
+
' -d \'{"status": "done"}\'',
|
|
378
|
+
'```',
|
|
379
|
+
'',
|
|
380
|
+
'ToDoを先に作成する理由: チャットのコンテキストが圧縮されても、未完了のToDoは次のターンで自動的に再表示されます。',
|
|
381
|
+
'これにより、長時間の作業でも途中で何をすべきか見失うことがありません。',
|
|
382
|
+
''
|
|
383
|
+
)
|
|
384
|
+
}
|
|
385
|
+
|
|
356
386
|
if (context) {
|
|
357
387
|
switch (context.type) {
|
|
358
388
|
case 'skill':
|
|
@@ -568,7 +598,7 @@ function streamViaLegacyLlmCommand(res, prompt, sessionId, workspaceId, original
|
|
|
568
598
|
const child = spawn(binary, args, {
|
|
569
599
|
cwd: config.HOME_DIR,
|
|
570
600
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
571
|
-
timeout:
|
|
601
|
+
timeout: 3600000, // 60 min — allow long-running tasks to complete
|
|
572
602
|
})
|
|
573
603
|
|
|
574
604
|
// Track active child process for abort
|
package/linux/workflow-runner.js
CHANGED
|
@@ -92,7 +92,38 @@ async function executeWorkflowSession(workflow, executionId, skillNames, options
|
|
|
92
92
|
revisionContext = `## Revision Feedback\nThe reviewer requested changes to your previous output. Address the following feedback:\n${options.revisionFeedback}\n\n`
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
// Inject DAG input data context (data from upstream nodes)
|
|
96
|
+
let dagDataContext = ''
|
|
97
|
+
if (options.dagInputData && Object.keys(options.dagInputData).length > 0) {
|
|
98
|
+
dagDataContext = `## Input Data (from upstream nodes)\nThe following data was produced by upstream steps in this workflow. Use it as context for your task.\n\`\`\`json\n${JSON.stringify(options.dagInputData, null, 2)}\n\`\`\`\n\n`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Inject DAG contract context (input/output contracts from edges)
|
|
102
|
+
let contractContext = ''
|
|
103
|
+
if (options.dagInputContracts && options.dagInputContracts.length > 0) {
|
|
104
|
+
contractContext += '## Input Contracts\nThe input data above conforms to the following contract(s):\n'
|
|
105
|
+
for (const ic of options.dagInputContracts) {
|
|
106
|
+
contractContext += `### ${ic.contract_name}\n${ic.contract.description || ''}\n`
|
|
107
|
+
contractContext += '| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n'
|
|
108
|
+
for (const f of ic.contract.fields || []) {
|
|
109
|
+
contractContext += `| ${f.key} | ${f.type} | ${f.required ? 'Yes' : 'No'} | ${f.description || ''} |\n`
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
contractContext += '\n'
|
|
113
|
+
}
|
|
114
|
+
if (options.dagOutputContracts && options.dagOutputContracts.length > 0) {
|
|
115
|
+
contractContext += '## Output Contract\nYour output MUST conform to the following contract(s). Include all required fields in your execution report.\n'
|
|
116
|
+
for (const oc of options.dagOutputContracts) {
|
|
117
|
+
contractContext += `### ${oc.contract_name}\n${oc.contract.description || ''}\n`
|
|
118
|
+
contractContext += '| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n'
|
|
119
|
+
for (const f of oc.contract.fields || []) {
|
|
120
|
+
contractContext += `| ${f.key} | ${f.type} | ${f.required ? 'Yes' : 'No'} | ${f.description || ''} |\n`
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
contractContext += '\n'
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const prompt = `${rolePrefix}${revisionContext}${dagDataContext}${contractContext}Run the following skills in order: ${skillCommands}.`
|
|
96
127
|
|
|
97
128
|
// Exit code file to capture CLI result
|
|
98
129
|
const exitCodeFile = `/tmp/tmux-exit-${sessionName}`
|
package/package.json
CHANGED
package/win/minion-cli.ps1
CHANGED
|
@@ -1522,7 +1522,21 @@ function Invoke-Configure {
|
|
|
1522
1522
|
$bodyHash = @{}
|
|
1523
1523
|
if ($lanIp) { $bodyHash['ip_address'] = $lanIp; $bodyHash['internal_ip_address'] = $lanIp }
|
|
1524
1524
|
else { $bodyHash['internal_ip_address'] = [System.Net.Dns]::GetHostName() }
|
|
1525
|
-
|
|
1525
|
+
|
|
1526
|
+
# Collect machine specs
|
|
1527
|
+
$cpuInfo = Get-CimInstance Win32_Processor -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
1528
|
+
$osInfo = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue
|
|
1529
|
+
$diskInfo = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'" -ErrorAction SilentlyContinue
|
|
1530
|
+
$bodyHash['machine_specs'] = @{
|
|
1531
|
+
cpu_model = if ($cpuInfo) { $cpuInfo.Name } else { 'unknown' }
|
|
1532
|
+
cpu_cores = if ($cpuInfo) { $cpuInfo.NumberOfLogicalProcessors } else { 0 }
|
|
1533
|
+
memory_mb = if ($osInfo) { [math]::Round($osInfo.TotalVisibleMemorySize / 1024) } else { 0 }
|
|
1534
|
+
disk_gb = if ($diskInfo) { [math]::Round($diskInfo.Size / 1GB) } else { 0 }
|
|
1535
|
+
os = if ($osInfo) { $osInfo.Caption } else { 'Windows' }
|
|
1536
|
+
arch = $env:PROCESSOR_ARCHITECTURE
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
$body = $bodyHash | ConvertTo-Json -Depth 3
|
|
1526
1540
|
Invoke-RestMethod -Uri "$HqUrl/api/minion/setup-complete" -Method Post -Headers $headers -Body $body -ErrorAction Stop | Out-Null
|
|
1527
1541
|
Write-Detail "HQ notified"
|
|
1528
1542
|
}
|
package/win/routes/chat.js
CHANGED
|
@@ -416,6 +416,36 @@ async function buildContextPrefix(message, context, sessionId, workspaceId) {
|
|
|
416
416
|
)
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
+
// Task planning guidance — instruct Claude to create TODOs before starting work
|
|
420
|
+
if (!sessionId) {
|
|
421
|
+
const port = require('../../core/config').config.AGENT_PORT
|
|
422
|
+
parts.push(
|
|
423
|
+
'[作業手順 — 必ず守ること]',
|
|
424
|
+
'依頼された作業に着手する**前に**、以下の手順を踏むこと:',
|
|
425
|
+
'',
|
|
426
|
+
'1. 依頼内容を分析し、必要なステップをToDoとして登録する',
|
|
427
|
+
'2. 各ステップを完了するたびに、ToDoのstatusを `done` に更新する',
|
|
428
|
+
'3. すべてのToDoが完了したら、ユーザーに完了報告する',
|
|
429
|
+
'',
|
|
430
|
+
'ToDo APIの使い方:',
|
|
431
|
+
'```bash',
|
|
432
|
+
'# ToDo作成(session_idは後でセッションIDが判明してから設定)',
|
|
433
|
+
`curl -X POST http://localhost:${port}/api/todos \\`,
|
|
434
|
+
' -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \\',
|
|
435
|
+
' -d \'{"title": "ステップの説明", "session_id": "SESSION_ID", "priority": "normal"}\'',
|
|
436
|
+
'',
|
|
437
|
+
'# ToDo完了',
|
|
438
|
+
`curl -X PUT http://localhost:${port}/api/todos/{id} \\`,
|
|
439
|
+
' -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \\',
|
|
440
|
+
' -d \'{"status": "done"}\'',
|
|
441
|
+
'```',
|
|
442
|
+
'',
|
|
443
|
+
'ToDoを先に作成する理由: チャットのコンテキストが圧縮されても、未完了のToDoは次のターンで自動的に再表示されます。',
|
|
444
|
+
'これにより、長時間の作業でも途中で何をすべきか見失うことがありません。',
|
|
445
|
+
''
|
|
446
|
+
)
|
|
447
|
+
}
|
|
448
|
+
|
|
419
449
|
if (context) {
|
|
420
450
|
switch (context.type) {
|
|
421
451
|
case 'skill':
|
|
@@ -602,7 +632,7 @@ function streamViaLegacyLlmCommand(res, prompt, sessionId, workspaceId, original
|
|
|
602
632
|
const child = spawn(binary, args, {
|
|
603
633
|
cwd: config.HOME_DIR,
|
|
604
634
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
605
|
-
timeout:
|
|
635
|
+
timeout: 3600000, // 60 min — allow long-running tasks to complete
|
|
606
636
|
shell: true, // Required on Windows to resolve .cmd shims (e.g. claude.cmd)
|
|
607
637
|
})
|
|
608
638
|
|
package/win/workflow-runner.js
CHANGED
|
@@ -99,7 +99,38 @@ async function executeWorkflowSession(workflow, executionId, skillNames, options
|
|
|
99
99
|
revisionContext = `## Revision Feedback\nThe reviewer requested changes to your previous output. Address the following feedback:\n${options.revisionFeedback}\n\n`
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
// Inject DAG input data context (data from upstream nodes)
|
|
103
|
+
let dagDataContext = ''
|
|
104
|
+
if (options.dagInputData && Object.keys(options.dagInputData).length > 0) {
|
|
105
|
+
dagDataContext = `## Input Data (from upstream nodes)\nThe following data was produced by upstream steps in this workflow. Use it as context for your task.\n\`\`\`json\n${JSON.stringify(options.dagInputData, null, 2)}\n\`\`\`\n\n`
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Inject DAG contract context (input/output contracts from edges)
|
|
109
|
+
let contractContext = ''
|
|
110
|
+
if (options.dagInputContracts && options.dagInputContracts.length > 0) {
|
|
111
|
+
contractContext += '## Input Contracts\nThe input data above conforms to the following contract(s):\n'
|
|
112
|
+
for (const ic of options.dagInputContracts) {
|
|
113
|
+
contractContext += `### ${ic.contract_name}\n${ic.contract.description || ''}\n`
|
|
114
|
+
contractContext += '| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n'
|
|
115
|
+
for (const f of ic.contract.fields || []) {
|
|
116
|
+
contractContext += `| ${f.key} | ${f.type} | ${f.required ? 'Yes' : 'No'} | ${f.description || ''} |\n`
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
contractContext += '\n'
|
|
120
|
+
}
|
|
121
|
+
if (options.dagOutputContracts && options.dagOutputContracts.length > 0) {
|
|
122
|
+
contractContext += '## Output Contract\nYour output MUST conform to the following contract(s). Include all required fields in your execution report.\n'
|
|
123
|
+
for (const oc of options.dagOutputContracts) {
|
|
124
|
+
contractContext += `### ${oc.contract_name}\n${oc.contract.description || ''}\n`
|
|
125
|
+
contractContext += '| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n'
|
|
126
|
+
for (const f of oc.contract.fields || []) {
|
|
127
|
+
contractContext += `| ${f.key} | ${f.type} | ${f.required ? 'Yes' : 'No'} | ${f.description || ''} |\n`
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
contractContext += '\n'
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const prompt = `${rolePrefix}${revisionContext}${dagDataContext}${contractContext}Run the following skills in order: ${skillCommands}.`
|
|
103
134
|
|
|
104
135
|
const logFile = logManager.getLogPath(executionId)
|
|
105
136
|
|
|
@@ -187,7 +187,7 @@ function streamLlmResponse(res, prompt, sessionId) {
|
|
|
187
187
|
const child = spawn(binary, args, {
|
|
188
188
|
cwd: HOME_DIR,
|
|
189
189
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
190
|
-
timeout:
|
|
190
|
+
timeout: 3600000, // 60 min — allow long-running tasks to complete
|
|
191
191
|
shell: true,
|
|
192
192
|
})
|
|
193
193
|
|