Merge upstream/main: resolve conflicts preserving local customizations
Merge 207 upstream commits (up to v1.2.41) while keeping Apple Container runtime support and MiniMax model forwarding. Remove deleted telegram.ts and credential-proxy.ts imports, widen CONTAINER_RUNTIME_BIN type for Apple Container detection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { execFile } from 'child_process';
|
||||
import { query, HookCallback, PreCompactHookInput } from '@anthropic-ai/claude-agent-sdk';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
@@ -27,6 +28,7 @@ interface ContainerInput {
|
||||
isMain: boolean;
|
||||
isScheduledTask?: boolean;
|
||||
assistantName?: string;
|
||||
script?: string;
|
||||
}
|
||||
|
||||
interface ContainerOutput {
|
||||
@@ -464,6 +466,55 @@ async function runQuery(
|
||||
return { newSessionId, lastAssistantUuid, closedDuringQuery };
|
||||
}
|
||||
|
||||
interface ScriptResult {
|
||||
wakeAgent: boolean;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
const SCRIPT_TIMEOUT_MS = 30_000;
|
||||
|
||||
async function runScript(script: string): Promise<ScriptResult | null> {
|
||||
const scriptPath = '/tmp/task-script.sh';
|
||||
fs.writeFileSync(scriptPath, script, { mode: 0o755 });
|
||||
|
||||
return new Promise((resolve) => {
|
||||
execFile('bash', [scriptPath], {
|
||||
timeout: SCRIPT_TIMEOUT_MS,
|
||||
maxBuffer: 1024 * 1024,
|
||||
env: process.env,
|
||||
}, (error, stdout, stderr) => {
|
||||
if (stderr) {
|
||||
log(`Script stderr: ${stderr.slice(0, 500)}`);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
log(`Script error: ${error.message}`);
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
// Parse last non-empty line of stdout as JSON
|
||||
const lines = stdout.trim().split('\n');
|
||||
const lastLine = lines[lines.length - 1];
|
||||
if (!lastLine) {
|
||||
log('Script produced no output');
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = JSON.parse(lastLine);
|
||||
if (typeof result.wakeAgent !== 'boolean') {
|
||||
log(`Script output missing wakeAgent boolean: ${lastLine.slice(0, 200)}`);
|
||||
return resolve(null);
|
||||
}
|
||||
resolve(result as ScriptResult);
|
||||
} catch {
|
||||
log(`Script output is not valid JSON: ${lastLine.slice(0, 200)}`);
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
let containerInput: ContainerInput;
|
||||
|
||||
@@ -505,6 +556,26 @@ async function main(): Promise<void> {
|
||||
prompt += '\n' + pending.join('\n');
|
||||
}
|
||||
|
||||
// Script phase: run script before waking agent
|
||||
if (containerInput.script && containerInput.isScheduledTask) {
|
||||
log('Running task script...');
|
||||
const scriptResult = await runScript(containerInput.script);
|
||||
|
||||
if (!scriptResult || !scriptResult.wakeAgent) {
|
||||
const reason = scriptResult ? 'wakeAgent=false' : 'script error/no output';
|
||||
log(`Script decided not to wake agent: ${reason}`);
|
||||
writeOutput({
|
||||
status: 'success',
|
||||
result: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Script says wake agent — enrich prompt with script data
|
||||
log(`Script wakeAgent=true, enriching prompt with data`);
|
||||
prompt = `[SCHEDULED TASK]\n\nScript output:\n${JSON.stringify(scriptResult.data, null, 2)}\n\nInstructions:\n${containerInput.prompt}`;
|
||||
}
|
||||
|
||||
// Query loop: run query → wait for IPC message → run new query → repeat
|
||||
let resumeAt: string | undefined;
|
||||
try {
|
||||
|
||||
@@ -91,6 +91,7 @@ SCHEDULE VALUE FORMAT (all times are LOCAL timezone):
|
||||
schedule_value: z.string().describe('cron: "*/5 * * * *" | interval: milliseconds like "300000" | once: local timestamp like "2026-02-01T15:30:00" (no Z suffix!)'),
|
||||
context_mode: z.enum(['group', 'isolated']).default('group').describe('group=runs with chat history and memory, isolated=fresh session (include context in prompt)'),
|
||||
target_group_jid: z.string().optional().describe('(Main group only) JID of the group to schedule the task for. Defaults to the current group.'),
|
||||
script: z.string().optional().describe('Optional bash script to run before waking the agent. Script must output JSON on the last line of stdout: { "wakeAgent": boolean, "data"?: any }. If wakeAgent is false, the agent is not called. Test your script with bash -c "..." before scheduling.'),
|
||||
},
|
||||
async (args) => {
|
||||
// Validate schedule_value before writing IPC
|
||||
@@ -136,6 +137,7 @@ SCHEDULE VALUE FORMAT (all times are LOCAL timezone):
|
||||
type: 'schedule_task',
|
||||
taskId,
|
||||
prompt: args.prompt,
|
||||
script: args.script || undefined,
|
||||
schedule_type: args.schedule_type,
|
||||
schedule_value: args.schedule_value,
|
||||
context_mode: args.context_mode || 'group',
|
||||
@@ -255,6 +257,7 @@ server.tool(
|
||||
prompt: z.string().optional().describe('New prompt for the task'),
|
||||
schedule_type: z.enum(['cron', 'interval', 'once']).optional().describe('New schedule type'),
|
||||
schedule_value: z.string().optional().describe('New schedule value (see schedule_task for format)'),
|
||||
script: z.string().optional().describe('New script for the task. Set to empty string to remove the script.'),
|
||||
},
|
||||
async (args) => {
|
||||
// Validate schedule_value if provided
|
||||
@@ -288,6 +291,7 @@ server.tool(
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
if (args.prompt !== undefined) data.prompt = args.prompt;
|
||||
if (args.script !== undefined) data.script = args.script;
|
||||
if (args.schedule_type !== undefined) data.schedule_type = args.schedule_type;
|
||||
if (args.schedule_value !== undefined) data.schedule_value = args.schedule_value;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user