@oyasmi/pipiclaw 0.3.1 → 0.3.3
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 +55 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +67 -9
- package/dist/agent.js.map +1 -1
- package/dist/dingtalk.d.ts +1 -1
- package/dist/dingtalk.d.ts.map +1 -1
- package/dist/dingtalk.js +26 -4
- package/dist/dingtalk.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +11 -0
- package/dist/events.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +106 -39
- package/dist/main.js.map +1 -1
- package/dist/paths.d.ts +2 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +2 -0
- package/dist/paths.js.map +1 -1
- package/dist/prompt-builder.d.ts +4 -1
- package/dist/prompt-builder.d.ts.map +1 -1
- package/dist/prompt-builder.js +30 -1
- package/dist/prompt-builder.js.map +1 -1
- package/dist/sandbox.d.ts +1 -0
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +56 -13
- package/dist/sandbox.js.map +1 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +47 -7
- package/dist/store.js.map +1 -1
- package/dist/sub-agents.d.ts +47 -0
- package/dist/sub-agents.d.ts.map +1 -0
- package/dist/sub-agents.js +228 -0
- package/dist/sub-agents.js.map +1 -0
- package/dist/tools/bash.d.ts +4 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +3 -2
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +2 -6
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/index.d.ts +10 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +15 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/subagent.d.ts +52 -0
- package/dist/tools/subagent.d.ts.map +1 -0
- package/dist/tools/subagent.js +245 -0
- package/dist/tools/subagent.js.map +1 -0
- package/dist/tools/write-content.d.ts +5 -0
- package/dist/tools/write-content.d.ts.map +1 -0
- package/dist/tools/write-content.js +30 -0
- package/dist/tools/write-content.js.map +1 -0
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +2 -10
- package/dist/tools/write.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -107,7 +107,7 @@ function bootstrapAppHome() {
|
|
|
107
107
|
mkdirSync(WORKSPACE_DIR, { recursive: true });
|
|
108
108
|
created.push("workspace/");
|
|
109
109
|
}
|
|
110
|
-
for (const dir of ["skills", "events"]) {
|
|
110
|
+
for (const dir of ["skills", "events", "sub-agents"]) {
|
|
111
111
|
const dirPath = join(WORKSPACE_DIR, dir);
|
|
112
112
|
if (!existsSync(dirPath)) {
|
|
113
113
|
mkdirSync(dirPath, { recursive: true });
|
|
@@ -225,6 +225,11 @@ const dingtalkConfig = loadConfig();
|
|
|
225
225
|
dingtalkConfig.stateDir = WORKSPACE_DIR;
|
|
226
226
|
await validateSandbox(sandbox);
|
|
227
227
|
const channelStates = new Map();
|
|
228
|
+
const activeTasks = new Set();
|
|
229
|
+
const SHUTDOWN_WAIT_MS = 15000;
|
|
230
|
+
const SHUTDOWN_ABORT_WAIT_MS = 5000;
|
|
231
|
+
let shuttingDown = false;
|
|
232
|
+
let shutdownPromise = null;
|
|
228
233
|
function getState(channelId) {
|
|
229
234
|
let state = channelStates.get(channelId);
|
|
230
235
|
if (!state) {
|
|
@@ -249,11 +254,16 @@ const handler = {
|
|
|
249
254
|
const state = channelStates.get(channelId);
|
|
250
255
|
if (state?.running) {
|
|
251
256
|
state.stopRequested = true;
|
|
252
|
-
state.runner.abort()
|
|
257
|
+
void state.runner.abort().catch((err) => {
|
|
258
|
+
log.logWarning(`[${channelId}] Failed to abort run`, err instanceof Error ? err.message : String(err));
|
|
259
|
+
});
|
|
253
260
|
log.logInfo(`[${channelId}] Stop requested`);
|
|
254
261
|
}
|
|
255
262
|
},
|
|
256
263
|
async handleBusyMessage(event, bot, mode, queueText) {
|
|
264
|
+
if (shuttingDown) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
257
267
|
const state = getState(event.channelId);
|
|
258
268
|
const trimmedQueueText = queueText.trim();
|
|
259
269
|
await state.store.logMessage(event.channelId, {
|
|
@@ -288,55 +298,112 @@ const handler = {
|
|
|
288
298
|
}
|
|
289
299
|
},
|
|
290
300
|
async handleEvent(event, bot, _isEvent) {
|
|
301
|
+
if (shuttingDown) {
|
|
302
|
+
log.logInfo(`[${event.channelId}] Ignoring event during shutdown`);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
291
305
|
const state = getState(event.channelId);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
306
|
+
const task = (async () => {
|
|
307
|
+
state.running = true;
|
|
308
|
+
state.stopRequested = false;
|
|
309
|
+
await state.store.logMessage(event.channelId, {
|
|
310
|
+
date: new Date().toISOString(),
|
|
311
|
+
ts: event.ts,
|
|
312
|
+
user: event.user,
|
|
313
|
+
userName: event.userName,
|
|
314
|
+
text: event.text,
|
|
315
|
+
isBot: false,
|
|
316
|
+
});
|
|
317
|
+
try {
|
|
318
|
+
const ctx = createDingTalkContext(event, bot, state.store);
|
|
319
|
+
const builtInCommand = parseBuiltInCommand(event.text);
|
|
320
|
+
if (builtInCommand) {
|
|
321
|
+
log.logInfo(`[${event.channelId}] Executing command: ${builtInCommand.rawText}`);
|
|
322
|
+
await state.runner.handleBuiltinCommand(ctx, builtInCommand);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
log.logInfo(`[${event.channelId}] Starting run: ${event.text.substring(0, 50)}`);
|
|
326
|
+
const result = await state.runner.run(ctx, state.store);
|
|
327
|
+
if (result.stopReason === "aborted" && state.stopRequested) {
|
|
328
|
+
log.logInfo(`[${event.channelId}] Stopped`);
|
|
329
|
+
}
|
|
309
330
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (result.stopReason === "aborted" && state.stopRequested) {
|
|
313
|
-
log.logInfo(`[${event.channelId}] Stopped`);
|
|
331
|
+
catch (err) {
|
|
332
|
+
log.logWarning(`[${event.channelId}] Run error`, err instanceof Error ? err.message : String(err));
|
|
314
333
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
334
|
+
finally {
|
|
335
|
+
state.running = false;
|
|
336
|
+
}
|
|
337
|
+
})();
|
|
338
|
+
activeTasks.add(task);
|
|
339
|
+
try {
|
|
340
|
+
await task;
|
|
318
341
|
}
|
|
319
342
|
finally {
|
|
320
|
-
|
|
343
|
+
activeTasks.delete(task);
|
|
321
344
|
}
|
|
322
345
|
},
|
|
323
346
|
};
|
|
324
347
|
log.logStartup(WORKSPACE_DIR, sandbox.type === "host" ? "host" : `docker:${sandbox.container}`);
|
|
325
|
-
if (!existsSync(WORKSPACE_DIR)) {
|
|
326
|
-
mkdirSync(WORKSPACE_DIR, { recursive: true });
|
|
327
|
-
}
|
|
328
348
|
const bot = new DingTalkBot(handler, dingtalkConfig);
|
|
329
349
|
const eventsWatcher = createEventsWatcher(WORKSPACE_DIR, bot);
|
|
330
350
|
eventsWatcher.start();
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
351
|
+
function waitForTasks(tasks, timeoutMs) {
|
|
352
|
+
if (tasks.length === 0) {
|
|
353
|
+
return Promise.resolve(true);
|
|
354
|
+
}
|
|
355
|
+
return Promise.race([
|
|
356
|
+
Promise.allSettled(tasks).then(() => true),
|
|
357
|
+
new Promise((resolve) => {
|
|
358
|
+
setTimeout(() => resolve(false), timeoutMs);
|
|
359
|
+
}),
|
|
360
|
+
]);
|
|
361
|
+
}
|
|
362
|
+
async function shutdown(signal) {
|
|
363
|
+
if (shutdownPromise) {
|
|
364
|
+
return shutdownPromise;
|
|
365
|
+
}
|
|
366
|
+
shutdownPromise = (async () => {
|
|
367
|
+
shuttingDown = true;
|
|
368
|
+
log.logInfo(`Shutting down (${signal})...`);
|
|
369
|
+
eventsWatcher.stop();
|
|
370
|
+
await bot.stop();
|
|
371
|
+
const runningTasks = Array.from(activeTasks);
|
|
372
|
+
if (runningTasks.length > 0) {
|
|
373
|
+
log.logInfo(`Waiting for ${runningTasks.length} active task(s) to finish`);
|
|
374
|
+
const completed = await waitForTasks(runningTasks, SHUTDOWN_WAIT_MS);
|
|
375
|
+
if (!completed) {
|
|
376
|
+
log.logWarning(`Shutdown grace period exceeded ${SHUTDOWN_WAIT_MS}ms, aborting active runs`);
|
|
377
|
+
const aborts = [];
|
|
378
|
+
for (const [channelId, state] of channelStates) {
|
|
379
|
+
if (!state.running)
|
|
380
|
+
continue;
|
|
381
|
+
state.stopRequested = true;
|
|
382
|
+
log.logInfo(`[${channelId}] Aborting active run for shutdown`);
|
|
383
|
+
aborts.push(state.runner.abort().catch((err) => {
|
|
384
|
+
log.logWarning(`[${channelId}] Failed to abort run during shutdown`, err instanceof Error ? err.message : String(err));
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
await Promise.allSettled(aborts);
|
|
388
|
+
const remainingTasks = Array.from(activeTasks);
|
|
389
|
+
if (remainingTasks.length > 0) {
|
|
390
|
+
const abortedCompleted = await waitForTasks(remainingTasks, SHUTDOWN_ABORT_WAIT_MS);
|
|
391
|
+
if (!abortedCompleted) {
|
|
392
|
+
log.logWarning(`Shutdown forced exit with ${remainingTasks.length} task(s) still active`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
})().finally(() => {
|
|
398
|
+
process.exit(0);
|
|
399
|
+
});
|
|
400
|
+
return shutdownPromise;
|
|
401
|
+
}
|
|
402
|
+
process.once("SIGINT", () => {
|
|
403
|
+
void shutdown("SIGINT");
|
|
335
404
|
});
|
|
336
|
-
process.
|
|
337
|
-
|
|
338
|
-
eventsWatcher.stop();
|
|
339
|
-
process.exit(0);
|
|
405
|
+
process.once("SIGTERM", () => {
|
|
406
|
+
void shutdown("SIGTERM");
|
|
340
407
|
});
|
|
341
|
-
bot.start();
|
|
408
|
+
void bot.start();
|
|
342
409
|
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { type AgentRunner, getOrCreateRunner } from \"./agent.js\";\nimport { parseBuiltInCommand } from \"./commands.js\";\nimport { createDingTalkContext } from \"./delivery.js\";\nimport {\n\ttype BusyMessageMode,\n\tDingTalkBot,\n\ttype DingTalkConfig,\n\ttype DingTalkEvent,\n\ttype DingTalkHandler,\n} from \"./dingtalk.js\";\nimport { createEventsWatcher } from \"./events.js\";\nimport * as log from \"./log.js\";\nimport { ensureChannelMemoryFilesSync } from \"./memory-files.js\";\nimport {\n\tAPP_HOME_DIR,\n\tAPP_NAME,\n\tAUTH_CONFIG_PATH,\n\tCHANNEL_CONFIG_PATH,\n\tMODELS_CONFIG_PATH,\n\tSETTINGS_CONFIG_PATH,\n\tWORKSPACE_DIR,\n} from \"./paths.js\";\nimport { parseSandboxArg, type SandboxConfig, validateSandbox } from \"./sandbox.js\";\nimport { ChannelStore } from \"./store.js\";\n\nif (process.env.DINGTALK_FORCE_PROXY !== \"true\") {\n\tdelete process.env.http_proxy;\n\tdelete process.env.https_proxy;\n\tdelete process.env.all_proxy;\n\tdelete process.env.HTTP_PROXY;\n\tdelete process.env.HTTPS_PROXY;\n\tdelete process.env.ALL_PROXY;\n}\n\ninterface ParsedArgs {\n\tsandbox: SandboxConfig;\n}\n\ninterface BootstrapResult {\n\tcreated: string[];\n\tchannelTemplateCreated: boolean;\n}\n\nconst DEFAULT_SOUL = `# SOUL.md\n\nConfigure Pipiclaw's identity, voice, and communication style here.\n\nSuggested sections:\n\n- Who the assistant is\n- Default language\n- Tone and personality\n- Reply style\n- Formatting preferences\n\nExample topics you may want to define:\n\n- \"Answer in Chinese by default.\"\n- \"Be concise and direct.\"\n- \"Prefer Markdown.\"\n- \"Act as an engineering assistant for our team.\"\n\nReplace this template with your actual identity prompt.\n`;\n\nconst DEFAULT_AGENT = `# AGENTS.md\n\nConfigure Pipiclaw's operating rules here.\n\nThis file should define behavior and workflow. Identity, tone, and personality belong in \\`SOUL.md\\`.\n\nSuggested sections:\n\n- Tool usage policy\n- Security constraints\n- Scheduling/reminder policy\n- Project-specific workflows\n- Things the assistant must always or never do\n\nReplace this template with your actual operating instructions.\n`;\n\nconst DEFAULT_MEMORY = `# Workspace Memory\n\nThis file stores stable workspace-level memory.\n\n- It is intended to be managed by a human administrator.\n- It is not automatically rewritten by normal runtime consolidation.\n- Store durable shared background here when it should apply across channels.\n- Keep this file focused on stable facts, policies, and shared context, not transient conversation history.\n\n## Shared Context\n\n<!-- Put team-wide or workspace-wide background here. -->\n\n## Tooling And Environment\n\n<!-- Put durable tool usage rules, environment assumptions, or shared operational conventions here. -->\n\n## Project Notes\n\n<!-- Put long-lived project facts here. -->\n`;\n\nconst CHANNEL_CONFIG_TEMPLATE = {\n\tclientId: \"your-dingtalk-client-id\",\n\tclientSecret: \"your-dingtalk-client-secret\",\n\trobotCode: \"your-robot-code\",\n\tcardTemplateId: \"your-card-template-id\",\n\tcardTemplateKey: \"content\",\n\tallowFrom: [\"your-staff-id\"],\n} satisfies DingTalkConfig;\n\nconst MODELS_CONFIG_TEMPLATE = { providers: {} };\n\nfunction writeTextFileIfMissing(path: string, content: string, label: string, created: string[]): boolean {\n\tif (existsSync(path)) {\n\t\treturn false;\n\t}\n\twriteFileSync(path, content, \"utf-8\");\n\tcreated.push(label);\n\treturn true;\n}\n\nfunction writeJsonFileIfMissing(path: string, value: unknown, label: string, created: string[]): boolean {\n\treturn writeTextFileIfMissing(path, `${JSON.stringify(value, null, 2)}\\n`, label, created);\n}\n\nfunction bootstrapAppHome(): BootstrapResult {\n\tconst created: string[] = [];\n\n\tif (!existsSync(APP_HOME_DIR)) {\n\t\tmkdirSync(APP_HOME_DIR, { recursive: true });\n\t\tcreated.push(\"app home\");\n\t}\n\tif (!existsSync(WORKSPACE_DIR)) {\n\t\tmkdirSync(WORKSPACE_DIR, { recursive: true });\n\t\tcreated.push(\"workspace/\");\n\t}\n\n\tfor (const dir of [\"skills\", \"events\"]) {\n\t\tconst dirPath = join(WORKSPACE_DIR, dir);\n\t\tif (!existsSync(dirPath)) {\n\t\t\tmkdirSync(dirPath, { recursive: true });\n\t\t\tcreated.push(`workspace/${dir}/`);\n\t\t}\n\t}\n\n\twriteTextFileIfMissing(join(WORKSPACE_DIR, \"SOUL.md\"), DEFAULT_SOUL, \"workspace/SOUL.md\", created);\n\twriteTextFileIfMissing(join(WORKSPACE_DIR, \"AGENTS.md\"), DEFAULT_AGENT, \"workspace/AGENTS.md\", created);\n\twriteTextFileIfMissing(join(WORKSPACE_DIR, \"MEMORY.md\"), DEFAULT_MEMORY, \"workspace/MEMORY.md\", created);\n\n\tconst channelTemplateCreated = writeJsonFileIfMissing(\n\t\tCHANNEL_CONFIG_PATH,\n\t\tCHANNEL_CONFIG_TEMPLATE,\n\t\t\"channel.json\",\n\t\tcreated,\n\t);\n\twriteJsonFileIfMissing(AUTH_CONFIG_PATH, {}, \"auth.json\", created);\n\twriteJsonFileIfMissing(MODELS_CONFIG_PATH, MODELS_CONFIG_TEMPLATE, \"models.json\", created);\n\twriteJsonFileIfMissing(SETTINGS_CONFIG_PATH, {}, \"settings.json\", created);\n\n\treturn { created, channelTemplateCreated };\n}\n\nfunction isPlaceholderString(value: string): boolean {\n\treturn value.trim().startsWith(\"your-\");\n}\n\nfunction listChannelConfigIssues(config: Partial<DingTalkConfig>): string[] {\n\tconst issues: string[] = [];\n\n\tif (!config.clientId) {\n\t\tissues.push(\"Missing required field `clientId`.\");\n\t} else if (isPlaceholderString(config.clientId)) {\n\t\tissues.push(\"Replace placeholder value for `clientId`.\");\n\t}\n\n\tif (!config.clientSecret) {\n\t\tissues.push(\"Missing required field `clientSecret`.\");\n\t} else if (isPlaceholderString(config.clientSecret)) {\n\t\tissues.push(\"Replace placeholder value for `clientSecret`.\");\n\t}\n\n\tif (config.robotCode && isPlaceholderString(config.robotCode)) {\n\t\tissues.push(\"Replace placeholder value for `robotCode`, or set it to an empty string to reuse `clientId`.\");\n\t}\n\n\tif (config.cardTemplateId && isPlaceholderString(config.cardTemplateId)) {\n\t\tissues.push(\n\t\t\t\"Replace placeholder value for `cardTemplateId`, or set it to an empty string to disable AI Card streaming.\",\n\t\t);\n\t}\n\n\tif (Array.isArray(config.allowFrom) && config.allowFrom.some((value) => isPlaceholderString(value))) {\n\t\tissues.push(\"Replace placeholder values in `allowFrom`, or set it to an empty array to allow all users.\");\n\t}\n\n\treturn issues;\n}\n\nfunction printBootstrapSummary(result: BootstrapResult): void {\n\tif (result.created.length === 0) {\n\t\treturn;\n\t}\n\n\tconsole.log(`Initialized ${APP_NAME} under ${APP_HOME_DIR}:`);\n\tfor (const item of result.created) {\n\t\tconsole.log(` - ${item}`);\n\t}\n\tconsole.log(\"\");\n}\n\nfunction loadConfig(): DingTalkConfig {\n\tlet parsed: DingTalkConfig;\n\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(CHANNEL_CONFIG_PATH, \"utf-8\")) as DingTalkConfig;\n\t} catch (err) {\n\t\tconsole.error(`Failed to parse configuration: ${CHANNEL_CONFIG_PATH}`);\n\t\tconsole.error(err instanceof Error ? err.message : String(err));\n\t\tprocess.exit(1);\n\t}\n\n\tconst issues = listChannelConfigIssues(parsed);\n\tif (issues.length > 0) {\n\t\tconsole.error(`Configuration is not ready: ${CHANNEL_CONFIG_PATH}`);\n\t\tfor (const issue of issues) {\n\t\t\tconsole.error(` - ${issue}`);\n\t\t}\n\t\tconsole.error(\"\");\n\t\tconsole.error(`Fill in ${CHANNEL_CONFIG_PATH} and run \\`${APP_NAME}\\` again.`);\n\t\tprocess.exit(1);\n\t}\n\n\tparsed.cardTemplateKey = parsed.cardTemplateKey || \"content\";\n\tparsed.robotCode = parsed.robotCode?.trim() ? parsed.robotCode : parsed.clientId;\n\tif (Array.isArray(parsed.allowFrom)) {\n\t\tparsed.allowFrom = parsed.allowFrom.filter((value) => value.trim().length > 0);\n\t}\n\n\treturn parsed;\n}\n\nfunction parseArgs(): ParsedArgs {\n\tconst args = process.argv.slice(2);\n\tlet sandbox: SandboxConfig = { type: \"host\" };\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\t\tif (arg.startsWith(\"--sandbox=\")) {\n\t\t\tsandbox = parseSandboxArg(arg.slice(\"--sandbox=\".length));\n\t\t} else if (arg === \"--sandbox\") {\n\t\t\tsandbox = parseSandboxArg(args[++i] || \"\");\n\t\t} else if (arg === \"--help\" || arg === \"-h\") {\n\t\t\tconsole.log(`Usage: ${APP_NAME} [options]`);\n\t\t\tconsole.log(\"\");\n\t\t\tconsole.log(\"Options:\");\n\t\t\tconsole.log(\" --sandbox=host Run tools on host (default)\");\n\t\t\tconsole.log(\" --sandbox=docker:<name> Run tools in Docker container\");\n\t\t\tconsole.log(\"\");\n\t\t\tconsole.log(`Config: ${CHANNEL_CONFIG_PATH}`);\n\t\t\tconsole.log(`Workspace: ${WORKSPACE_DIR}`);\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\treturn { sandbox };\n}\n\nconst parsedArgs = parseArgs();\nconst sandbox = parsedArgs.sandbox;\nconst bootstrapResult = bootstrapAppHome();\nprintBootstrapSummary(bootstrapResult);\n\nif (bootstrapResult.channelTemplateCreated) {\n\tconsole.error(`Fill in ${CHANNEL_CONFIG_PATH} and run \\`${APP_NAME}\\` again.`);\n\tprocess.exit(1);\n}\n\nconst dingtalkConfig = loadConfig();\ndingtalkConfig.stateDir = WORKSPACE_DIR;\n\nawait validateSandbox(sandbox);\n\ninterface ChannelState {\n\trunning: boolean;\n\trunner: AgentRunner;\n\tstore: ChannelStore;\n\tstopRequested: boolean;\n}\n\nconst channelStates = new Map<string, ChannelState>();\n\nfunction getState(channelId: string): ChannelState {\n\tlet state = channelStates.get(channelId);\n\tif (!state) {\n\t\tconst channelDir = join(WORKSPACE_DIR, channelId);\n\t\tensureChannelMemoryFilesSync(channelDir);\n\t\tstate = {\n\t\t\trunning: false,\n\t\t\trunner: getOrCreateRunner(sandbox, channelId, channelDir),\n\t\t\tstore: new ChannelStore({ workingDir: WORKSPACE_DIR }),\n\t\t\tstopRequested: false,\n\t\t};\n\t\tchannelStates.set(channelId, state);\n\t}\n\treturn state;\n}\n\nconst handler: DingTalkHandler = {\n\tisRunning(channelId: string): boolean {\n\t\tconst state = channelStates.get(channelId);\n\t\treturn state?.running ?? false;\n\t},\n\n\tasync handleStop(channelId: string, _bot: DingTalkBot): Promise<void> {\n\t\tconst state = channelStates.get(channelId);\n\t\tif (state?.running) {\n\t\t\tstate.stopRequested = true;\n\t\t\tstate.runner.abort();\n\t\t\tlog.logInfo(`[${channelId}] Stop requested`);\n\t\t}\n\t},\n\n\tasync handleBusyMessage(\n\t\tevent: DingTalkEvent,\n\t\tbot: DingTalkBot,\n\t\tmode: BusyMessageMode,\n\t\tqueueText: string,\n\t): Promise<void> {\n\t\tconst state = getState(event.channelId);\n\t\tconst trimmedQueueText = queueText.trim();\n\n\t\tawait state.store.logMessage(event.channelId, {\n\t\t\tdate: new Date().toISOString(),\n\t\t\tts: event.ts,\n\t\t\tuser: event.user,\n\t\t\tuserName: event.userName,\n\t\t\ttext: event.text,\n\t\t\tisBot: false,\n\t\t\tdeliveryMode: mode,\n\t\t\tskipContextSync: true,\n\t\t});\n\n\t\ttry {\n\t\t\tif (mode === \"followUp\") {\n\t\t\t\tawait state.runner.queueFollowUp(trimmedQueueText, event.userName);\n\t\t\t} else {\n\t\t\t\tawait state.runner.queueSteer(trimmedQueueText, event.userName);\n\t\t\t}\n\n\t\t\tconst confirmation =\n\t\t\t\tmode === \"followUp\"\n\t\t\t\t\t? \"Queued as follow-up. I’ll handle it after the current task completes.\"\n\t\t\t\t\t: event.text.trim().startsWith(\"/\")\n\t\t\t\t\t\t? \"Queued as steer. I’ll apply it after the current tool step finishes.\"\n\t\t\t\t\t\t: \"Queued as steer. I’ll apply this after the current tool step finishes. Use `/followup <message>` to queue it after completion.\";\n\t\t\tawait bot.sendPlain(event.channelId, confirmation);\n\t\t\tlog.logInfo(`[${event.channelId}] Queued ${mode}: ${trimmedQueueText.substring(0, 80)}`);\n\t\t} catch (err) {\n\t\t\tconst errMsg = err instanceof Error ? err.message : String(err);\n\t\t\tlog.logWarning(`[${event.channelId}] Failed to queue ${mode}`, errMsg);\n\t\t\tawait bot.sendPlain(event.channelId, `Could not queue this message: ${errMsg}`);\n\t\t}\n\t},\n\n\tasync handleEvent(event: DingTalkEvent, bot: DingTalkBot, _isEvent?: boolean): Promise<void> {\n\t\tconst state = getState(event.channelId);\n\n\t\tstate.running = true;\n\t\tstate.stopRequested = false;\n\n\t\tawait state.store.logMessage(event.channelId, {\n\t\t\tdate: new Date().toISOString(),\n\t\t\tts: event.ts,\n\t\t\tuser: event.user,\n\t\t\tuserName: event.userName,\n\t\t\ttext: event.text,\n\t\t\tisBot: false,\n\t\t});\n\n\t\ttry {\n\t\t\tconst ctx = createDingTalkContext(event, bot, state.store);\n\t\t\tconst builtInCommand = parseBuiltInCommand(event.text);\n\n\t\t\tif (builtInCommand) {\n\t\t\t\tlog.logInfo(`[${event.channelId}] Executing command: ${builtInCommand.rawText}`);\n\t\t\t\tawait state.runner.handleBuiltinCommand(ctx, builtInCommand);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlog.logInfo(`[${event.channelId}] Starting run: ${event.text.substring(0, 50)}`);\n\t\t\tconst result = await state.runner.run(ctx, state.store);\n\n\t\t\tif (result.stopReason === \"aborted\" && state.stopRequested) {\n\t\t\t\tlog.logInfo(`[${event.channelId}] Stopped`);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tlog.logWarning(`[${event.channelId}] Run error`, err instanceof Error ? err.message : String(err));\n\t\t} finally {\n\t\t\tstate.running = false;\n\t\t}\n\t},\n};\n\nlog.logStartup(WORKSPACE_DIR, sandbox.type === \"host\" ? \"host\" : `docker:${sandbox.container}`);\n\nif (!existsSync(WORKSPACE_DIR)) {\n\tmkdirSync(WORKSPACE_DIR, { recursive: true });\n}\n\nconst bot = new DingTalkBot(handler, dingtalkConfig);\nconst eventsWatcher = createEventsWatcher(WORKSPACE_DIR, bot);\neventsWatcher.start();\n\nprocess.on(\"SIGINT\", () => {\n\tlog.logInfo(\"Shutting down...\");\n\teventsWatcher.stop();\n\tprocess.exit(0);\n});\n\nprocess.on(\"SIGTERM\", () => {\n\tlog.logInfo(\"Shutting down...\");\n\teventsWatcher.stop();\n\tprocess.exit(0);\n});\n\nbot.start();\n"]}
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { type AgentRunner, getOrCreateRunner } from \"./agent.js\";\nimport { parseBuiltInCommand } from \"./commands.js\";\nimport { createDingTalkContext } from \"./delivery.js\";\nimport {\n\ttype BusyMessageMode,\n\tDingTalkBot,\n\ttype DingTalkConfig,\n\ttype DingTalkEvent,\n\ttype DingTalkHandler,\n} from \"./dingtalk.js\";\nimport { createEventsWatcher } from \"./events.js\";\nimport * as log from \"./log.js\";\nimport { ensureChannelMemoryFilesSync } from \"./memory-files.js\";\nimport {\n\tAPP_HOME_DIR,\n\tAPP_NAME,\n\tAUTH_CONFIG_PATH,\n\tCHANNEL_CONFIG_PATH,\n\tMODELS_CONFIG_PATH,\n\tSETTINGS_CONFIG_PATH,\n\tWORKSPACE_DIR,\n} from \"./paths.js\";\nimport { parseSandboxArg, type SandboxConfig, validateSandbox } from \"./sandbox.js\";\nimport { ChannelStore } from \"./store.js\";\n\nif (process.env.DINGTALK_FORCE_PROXY !== \"true\") {\n\tdelete process.env.http_proxy;\n\tdelete process.env.https_proxy;\n\tdelete process.env.all_proxy;\n\tdelete process.env.HTTP_PROXY;\n\tdelete process.env.HTTPS_PROXY;\n\tdelete process.env.ALL_PROXY;\n}\n\ninterface ParsedArgs {\n\tsandbox: SandboxConfig;\n}\n\ninterface BootstrapResult {\n\tcreated: string[];\n\tchannelTemplateCreated: boolean;\n}\n\nconst DEFAULT_SOUL = `# SOUL.md\n\nConfigure Pipiclaw's identity, voice, and communication style here.\n\nSuggested sections:\n\n- Who the assistant is\n- Default language\n- Tone and personality\n- Reply style\n- Formatting preferences\n\nExample topics you may want to define:\n\n- \"Answer in Chinese by default.\"\n- \"Be concise and direct.\"\n- \"Prefer Markdown.\"\n- \"Act as an engineering assistant for our team.\"\n\nReplace this template with your actual identity prompt.\n`;\n\nconst DEFAULT_AGENT = `# AGENTS.md\n\nConfigure Pipiclaw's operating rules here.\n\nThis file should define behavior and workflow. Identity, tone, and personality belong in \\`SOUL.md\\`.\n\nSuggested sections:\n\n- Tool usage policy\n- Security constraints\n- Scheduling/reminder policy\n- Project-specific workflows\n- Things the assistant must always or never do\n\nReplace this template with your actual operating instructions.\n`;\n\nconst DEFAULT_MEMORY = `# Workspace Memory\n\nThis file stores stable workspace-level memory.\n\n- It is intended to be managed by a human administrator.\n- It is not automatically rewritten by normal runtime consolidation.\n- Store durable shared background here when it should apply across channels.\n- Keep this file focused on stable facts, policies, and shared context, not transient conversation history.\n\n## Shared Context\n\n<!-- Put team-wide or workspace-wide background here. -->\n\n## Tooling And Environment\n\n<!-- Put durable tool usage rules, environment assumptions, or shared operational conventions here. -->\n\n## Project Notes\n\n<!-- Put long-lived project facts here. -->\n`;\n\nconst CHANNEL_CONFIG_TEMPLATE = {\n\tclientId: \"your-dingtalk-client-id\",\n\tclientSecret: \"your-dingtalk-client-secret\",\n\trobotCode: \"your-robot-code\",\n\tcardTemplateId: \"your-card-template-id\",\n\tcardTemplateKey: \"content\",\n\tallowFrom: [\"your-staff-id\"],\n} satisfies DingTalkConfig;\n\nconst MODELS_CONFIG_TEMPLATE = { providers: {} };\n\nfunction writeTextFileIfMissing(path: string, content: string, label: string, created: string[]): boolean {\n\tif (existsSync(path)) {\n\t\treturn false;\n\t}\n\twriteFileSync(path, content, \"utf-8\");\n\tcreated.push(label);\n\treturn true;\n}\n\nfunction writeJsonFileIfMissing(path: string, value: unknown, label: string, created: string[]): boolean {\n\treturn writeTextFileIfMissing(path, `${JSON.stringify(value, null, 2)}\\n`, label, created);\n}\n\nfunction bootstrapAppHome(): BootstrapResult {\n\tconst created: string[] = [];\n\n\tif (!existsSync(APP_HOME_DIR)) {\n\t\tmkdirSync(APP_HOME_DIR, { recursive: true });\n\t\tcreated.push(\"app home\");\n\t}\n\tif (!existsSync(WORKSPACE_DIR)) {\n\t\tmkdirSync(WORKSPACE_DIR, { recursive: true });\n\t\tcreated.push(\"workspace/\");\n\t}\n\n\tfor (const dir of [\"skills\", \"events\", \"sub-agents\"]) {\n\t\tconst dirPath = join(WORKSPACE_DIR, dir);\n\t\tif (!existsSync(dirPath)) {\n\t\t\tmkdirSync(dirPath, { recursive: true });\n\t\t\tcreated.push(`workspace/${dir}/`);\n\t\t}\n\t}\n\n\twriteTextFileIfMissing(join(WORKSPACE_DIR, \"SOUL.md\"), DEFAULT_SOUL, \"workspace/SOUL.md\", created);\n\twriteTextFileIfMissing(join(WORKSPACE_DIR, \"AGENTS.md\"), DEFAULT_AGENT, \"workspace/AGENTS.md\", created);\n\twriteTextFileIfMissing(join(WORKSPACE_DIR, \"MEMORY.md\"), DEFAULT_MEMORY, \"workspace/MEMORY.md\", created);\n\n\tconst channelTemplateCreated = writeJsonFileIfMissing(\n\t\tCHANNEL_CONFIG_PATH,\n\t\tCHANNEL_CONFIG_TEMPLATE,\n\t\t\"channel.json\",\n\t\tcreated,\n\t);\n\twriteJsonFileIfMissing(AUTH_CONFIG_PATH, {}, \"auth.json\", created);\n\twriteJsonFileIfMissing(MODELS_CONFIG_PATH, MODELS_CONFIG_TEMPLATE, \"models.json\", created);\n\twriteJsonFileIfMissing(SETTINGS_CONFIG_PATH, {}, \"settings.json\", created);\n\n\treturn { created, channelTemplateCreated };\n}\n\nfunction isPlaceholderString(value: string): boolean {\n\treturn value.trim().startsWith(\"your-\");\n}\n\nfunction listChannelConfigIssues(config: Partial<DingTalkConfig>): string[] {\n\tconst issues: string[] = [];\n\n\tif (!config.clientId) {\n\t\tissues.push(\"Missing required field `clientId`.\");\n\t} else if (isPlaceholderString(config.clientId)) {\n\t\tissues.push(\"Replace placeholder value for `clientId`.\");\n\t}\n\n\tif (!config.clientSecret) {\n\t\tissues.push(\"Missing required field `clientSecret`.\");\n\t} else if (isPlaceholderString(config.clientSecret)) {\n\t\tissues.push(\"Replace placeholder value for `clientSecret`.\");\n\t}\n\n\tif (config.robotCode && isPlaceholderString(config.robotCode)) {\n\t\tissues.push(\"Replace placeholder value for `robotCode`, or set it to an empty string to reuse `clientId`.\");\n\t}\n\n\tif (config.cardTemplateId && isPlaceholderString(config.cardTemplateId)) {\n\t\tissues.push(\n\t\t\t\"Replace placeholder value for `cardTemplateId`, or set it to an empty string to disable AI Card streaming.\",\n\t\t);\n\t}\n\n\tif (Array.isArray(config.allowFrom) && config.allowFrom.some((value) => isPlaceholderString(value))) {\n\t\tissues.push(\"Replace placeholder values in `allowFrom`, or set it to an empty array to allow all users.\");\n\t}\n\n\treturn issues;\n}\n\nfunction printBootstrapSummary(result: BootstrapResult): void {\n\tif (result.created.length === 0) {\n\t\treturn;\n\t}\n\n\tconsole.log(`Initialized ${APP_NAME} under ${APP_HOME_DIR}:`);\n\tfor (const item of result.created) {\n\t\tconsole.log(` - ${item}`);\n\t}\n\tconsole.log(\"\");\n}\n\nfunction loadConfig(): DingTalkConfig {\n\tlet parsed: DingTalkConfig;\n\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(CHANNEL_CONFIG_PATH, \"utf-8\")) as DingTalkConfig;\n\t} catch (err) {\n\t\tconsole.error(`Failed to parse configuration: ${CHANNEL_CONFIG_PATH}`);\n\t\tconsole.error(err instanceof Error ? err.message : String(err));\n\t\tprocess.exit(1);\n\t}\n\n\tconst issues = listChannelConfigIssues(parsed);\n\tif (issues.length > 0) {\n\t\tconsole.error(`Configuration is not ready: ${CHANNEL_CONFIG_PATH}`);\n\t\tfor (const issue of issues) {\n\t\t\tconsole.error(` - ${issue}`);\n\t\t}\n\t\tconsole.error(\"\");\n\t\tconsole.error(`Fill in ${CHANNEL_CONFIG_PATH} and run \\`${APP_NAME}\\` again.`);\n\t\tprocess.exit(1);\n\t}\n\n\tparsed.cardTemplateKey = parsed.cardTemplateKey || \"content\";\n\tparsed.robotCode = parsed.robotCode?.trim() ? parsed.robotCode : parsed.clientId;\n\tif (Array.isArray(parsed.allowFrom)) {\n\t\tparsed.allowFrom = parsed.allowFrom.filter((value) => value.trim().length > 0);\n\t}\n\n\treturn parsed;\n}\n\nfunction parseArgs(): ParsedArgs {\n\tconst args = process.argv.slice(2);\n\tlet sandbox: SandboxConfig = { type: \"host\" };\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\t\tif (arg.startsWith(\"--sandbox=\")) {\n\t\t\tsandbox = parseSandboxArg(arg.slice(\"--sandbox=\".length));\n\t\t} else if (arg === \"--sandbox\") {\n\t\t\tsandbox = parseSandboxArg(args[++i] || \"\");\n\t\t} else if (arg === \"--help\" || arg === \"-h\") {\n\t\t\tconsole.log(`Usage: ${APP_NAME} [options]`);\n\t\t\tconsole.log(\"\");\n\t\t\tconsole.log(\"Options:\");\n\t\t\tconsole.log(\" --sandbox=host Run tools on host (default)\");\n\t\t\tconsole.log(\" --sandbox=docker:<name> Run tools in Docker container\");\n\t\t\tconsole.log(\"\");\n\t\t\tconsole.log(`Config: ${CHANNEL_CONFIG_PATH}`);\n\t\t\tconsole.log(`Workspace: ${WORKSPACE_DIR}`);\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\treturn { sandbox };\n}\n\nconst parsedArgs = parseArgs();\nconst sandbox = parsedArgs.sandbox;\nconst bootstrapResult = bootstrapAppHome();\nprintBootstrapSummary(bootstrapResult);\n\nif (bootstrapResult.channelTemplateCreated) {\n\tconsole.error(`Fill in ${CHANNEL_CONFIG_PATH} and run \\`${APP_NAME}\\` again.`);\n\tprocess.exit(1);\n}\n\nconst dingtalkConfig = loadConfig();\ndingtalkConfig.stateDir = WORKSPACE_DIR;\n\nawait validateSandbox(sandbox);\n\ninterface ChannelState {\n\trunning: boolean;\n\trunner: AgentRunner;\n\tstore: ChannelStore;\n\tstopRequested: boolean;\n}\n\nconst channelStates = new Map<string, ChannelState>();\nconst activeTasks = new Set<Promise<void>>();\nconst SHUTDOWN_WAIT_MS = 15000;\nconst SHUTDOWN_ABORT_WAIT_MS = 5000;\nlet shuttingDown = false;\nlet shutdownPromise: Promise<void> | null = null;\n\nfunction getState(channelId: string): ChannelState {\n\tlet state = channelStates.get(channelId);\n\tif (!state) {\n\t\tconst channelDir = join(WORKSPACE_DIR, channelId);\n\t\tensureChannelMemoryFilesSync(channelDir);\n\t\tstate = {\n\t\t\trunning: false,\n\t\t\trunner: getOrCreateRunner(sandbox, channelId, channelDir),\n\t\t\tstore: new ChannelStore({ workingDir: WORKSPACE_DIR }),\n\t\t\tstopRequested: false,\n\t\t};\n\t\tchannelStates.set(channelId, state);\n\t}\n\treturn state;\n}\n\nconst handler: DingTalkHandler = {\n\tisRunning(channelId: string): boolean {\n\t\tconst state = channelStates.get(channelId);\n\t\treturn state?.running ?? false;\n\t},\n\n\tasync handleStop(channelId: string, _bot: DingTalkBot): Promise<void> {\n\t\tconst state = channelStates.get(channelId);\n\t\tif (state?.running) {\n\t\t\tstate.stopRequested = true;\n\t\t\tvoid state.runner.abort().catch((err) => {\n\t\t\t\tlog.logWarning(`[${channelId}] Failed to abort run`, err instanceof Error ? err.message : String(err));\n\t\t\t});\n\t\t\tlog.logInfo(`[${channelId}] Stop requested`);\n\t\t}\n\t},\n\n\tasync handleBusyMessage(\n\t\tevent: DingTalkEvent,\n\t\tbot: DingTalkBot,\n\t\tmode: BusyMessageMode,\n\t\tqueueText: string,\n\t): Promise<void> {\n\t\tif (shuttingDown) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst state = getState(event.channelId);\n\t\tconst trimmedQueueText = queueText.trim();\n\n\t\tawait state.store.logMessage(event.channelId, {\n\t\t\tdate: new Date().toISOString(),\n\t\t\tts: event.ts,\n\t\t\tuser: event.user,\n\t\t\tuserName: event.userName,\n\t\t\ttext: event.text,\n\t\t\tisBot: false,\n\t\t\tdeliveryMode: mode,\n\t\t\tskipContextSync: true,\n\t\t});\n\n\t\ttry {\n\t\t\tif (mode === \"followUp\") {\n\t\t\t\tawait state.runner.queueFollowUp(trimmedQueueText, event.userName);\n\t\t\t} else {\n\t\t\t\tawait state.runner.queueSteer(trimmedQueueText, event.userName);\n\t\t\t}\n\n\t\t\tconst confirmation =\n\t\t\t\tmode === \"followUp\"\n\t\t\t\t\t? \"Queued as follow-up. I’ll handle it after the current task completes.\"\n\t\t\t\t\t: event.text.trim().startsWith(\"/\")\n\t\t\t\t\t\t? \"Queued as steer. I’ll apply it after the current tool step finishes.\"\n\t\t\t\t\t\t: \"Queued as steer. I’ll apply this after the current tool step finishes. Use `/followup <message>` to queue it after completion.\";\n\t\t\tawait bot.sendPlain(event.channelId, confirmation);\n\t\t\tlog.logInfo(`[${event.channelId}] Queued ${mode}: ${trimmedQueueText.substring(0, 80)}`);\n\t\t} catch (err) {\n\t\t\tconst errMsg = err instanceof Error ? err.message : String(err);\n\t\t\tlog.logWarning(`[${event.channelId}] Failed to queue ${mode}`, errMsg);\n\t\t\tawait bot.sendPlain(event.channelId, `Could not queue this message: ${errMsg}`);\n\t\t}\n\t},\n\n\tasync handleEvent(event: DingTalkEvent, bot: DingTalkBot, _isEvent?: boolean): Promise<void> {\n\t\tif (shuttingDown) {\n\t\t\tlog.logInfo(`[${event.channelId}] Ignoring event during shutdown`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst state = getState(event.channelId);\n\t\tconst task = (async () => {\n\t\t\tstate.running = true;\n\t\t\tstate.stopRequested = false;\n\n\t\t\tawait state.store.logMessage(event.channelId, {\n\t\t\t\tdate: new Date().toISOString(),\n\t\t\t\tts: event.ts,\n\t\t\t\tuser: event.user,\n\t\t\t\tuserName: event.userName,\n\t\t\t\ttext: event.text,\n\t\t\t\tisBot: false,\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tconst ctx = createDingTalkContext(event, bot, state.store);\n\t\t\t\tconst builtInCommand = parseBuiltInCommand(event.text);\n\n\t\t\t\tif (builtInCommand) {\n\t\t\t\t\tlog.logInfo(`[${event.channelId}] Executing command: ${builtInCommand.rawText}`);\n\t\t\t\t\tawait state.runner.handleBuiltinCommand(ctx, builtInCommand);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlog.logInfo(`[${event.channelId}] Starting run: ${event.text.substring(0, 50)}`);\n\t\t\t\tconst result = await state.runner.run(ctx, state.store);\n\n\t\t\t\tif (result.stopReason === \"aborted\" && state.stopRequested) {\n\t\t\t\t\tlog.logInfo(`[${event.channelId}] Stopped`);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tlog.logWarning(`[${event.channelId}] Run error`, err instanceof Error ? err.message : String(err));\n\t\t\t} finally {\n\t\t\t\tstate.running = false;\n\t\t\t}\n\t\t})();\n\n\t\tactiveTasks.add(task);\n\t\ttry {\n\t\t\tawait task;\n\t\t} finally {\n\t\t\tactiveTasks.delete(task);\n\t\t}\n\t},\n};\n\nlog.logStartup(WORKSPACE_DIR, sandbox.type === \"host\" ? \"host\" : `docker:${sandbox.container}`);\n\nconst bot = new DingTalkBot(handler, dingtalkConfig);\nconst eventsWatcher = createEventsWatcher(WORKSPACE_DIR, bot);\neventsWatcher.start();\n\nfunction waitForTasks(tasks: Promise<void>[], timeoutMs: number): Promise<boolean> {\n\tif (tasks.length === 0) {\n\t\treturn Promise.resolve(true);\n\t}\n\n\treturn Promise.race([\n\t\tPromise.allSettled(tasks).then(() => true),\n\t\tnew Promise<boolean>((resolve) => {\n\t\t\tsetTimeout(() => resolve(false), timeoutMs);\n\t\t}),\n\t]);\n}\n\nasync function shutdown(signal: NodeJS.Signals): Promise<void> {\n\tif (shutdownPromise) {\n\t\treturn shutdownPromise;\n\t}\n\n\tshutdownPromise = (async () => {\n\t\tshuttingDown = true;\n\t\tlog.logInfo(`Shutting down (${signal})...`);\n\n\t\teventsWatcher.stop();\n\t\tawait bot.stop();\n\n\t\tconst runningTasks = Array.from(activeTasks);\n\t\tif (runningTasks.length > 0) {\n\t\t\tlog.logInfo(`Waiting for ${runningTasks.length} active task(s) to finish`);\n\t\t\tconst completed = await waitForTasks(runningTasks, SHUTDOWN_WAIT_MS);\n\n\t\t\tif (!completed) {\n\t\t\t\tlog.logWarning(`Shutdown grace period exceeded ${SHUTDOWN_WAIT_MS}ms, aborting active runs`);\n\t\t\t\tconst aborts: Promise<void>[] = [];\n\t\t\t\tfor (const [channelId, state] of channelStates) {\n\t\t\t\t\tif (!state.running) continue;\n\t\t\t\t\tstate.stopRequested = true;\n\t\t\t\t\tlog.logInfo(`[${channelId}] Aborting active run for shutdown`);\n\t\t\t\t\taborts.push(\n\t\t\t\t\t\tstate.runner.abort().catch((err) => {\n\t\t\t\t\t\t\tlog.logWarning(\n\t\t\t\t\t\t\t\t`[${channelId}] Failed to abort run during shutdown`,\n\t\t\t\t\t\t\t\terr instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tawait Promise.allSettled(aborts);\n\n\t\t\t\tconst remainingTasks = Array.from(activeTasks);\n\t\t\t\tif (remainingTasks.length > 0) {\n\t\t\t\t\tconst abortedCompleted = await waitForTasks(remainingTasks, SHUTDOWN_ABORT_WAIT_MS);\n\t\t\t\t\tif (!abortedCompleted) {\n\t\t\t\t\t\tlog.logWarning(`Shutdown forced exit with ${remainingTasks.length} task(s) still active`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})().finally(() => {\n\t\tprocess.exit(0);\n\t});\n\n\treturn shutdownPromise;\n}\n\nprocess.once(\"SIGINT\", () => {\n\tvoid shutdown(\"SIGINT\");\n});\n\nprocess.once(\"SIGTERM\", () => {\n\tvoid shutdown(\"SIGTERM\");\n});\n\nvoid bot.start();\n"]}
|
package/dist/paths.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export declare const APP_NAME = "pipiclaw";
|
|
2
2
|
export declare const APP_HOME_DIR: string;
|
|
3
3
|
export declare const WORKSPACE_DIR: string;
|
|
4
|
+
export declare const SUB_AGENTS_DIR_NAME = "sub-agents";
|
|
5
|
+
export declare const SUB_AGENTS_DIR: string;
|
|
4
6
|
export declare const CHANNEL_CONFIG_PATH: string;
|
|
5
7
|
export declare const AUTH_CONFIG_PATH: string;
|
|
6
8
|
export declare const MODELS_CONFIG_PATH: string;
|
package/dist/paths.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ,aAAa,CAAC;AACnC,eAAO,MAAM,YAAY,QAAmC,CAAC;AAC7D,eAAO,MAAM,aAAa,QAAkC,CAAC;AAC7D,eAAO,MAAM,mBAAmB,QAAqC,CAAC;AACtE,eAAO,MAAM,gBAAgB,QAAkC,CAAC;AAChE,eAAO,MAAM,kBAAkB,QAAoC,CAAC;AACpE,eAAO,MAAM,oBAAoB,QAAsC,CAAC","sourcesContent":["import { homedir } from \"os\";\nimport { join } from \"path\";\n\nexport const APP_NAME = \"pipiclaw\";\nexport const APP_HOME_DIR = join(homedir(), \".pi\", APP_NAME);\nexport const WORKSPACE_DIR = join(APP_HOME_DIR, \"workspace\");\nexport const CHANNEL_CONFIG_PATH = join(APP_HOME_DIR, \"channel.json\");\nexport const AUTH_CONFIG_PATH = join(APP_HOME_DIR, \"auth.json\");\nexport const MODELS_CONFIG_PATH = join(APP_HOME_DIR, \"models.json\");\nexport const SETTINGS_CONFIG_PATH = join(APP_HOME_DIR, \"settings.json\");\n"]}
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ,aAAa,CAAC;AACnC,eAAO,MAAM,YAAY,QAAmC,CAAC;AAC7D,eAAO,MAAM,aAAa,QAAkC,CAAC;AAC7D,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAChD,eAAO,MAAM,cAAc,QAA2C,CAAC;AACvE,eAAO,MAAM,mBAAmB,QAAqC,CAAC;AACtE,eAAO,MAAM,gBAAgB,QAAkC,CAAC;AAChE,eAAO,MAAM,kBAAkB,QAAoC,CAAC;AACpE,eAAO,MAAM,oBAAoB,QAAsC,CAAC","sourcesContent":["import { homedir } from \"os\";\nimport { join } from \"path\";\n\nexport const APP_NAME = \"pipiclaw\";\nexport const APP_HOME_DIR = join(homedir(), \".pi\", APP_NAME);\nexport const WORKSPACE_DIR = join(APP_HOME_DIR, \"workspace\");\nexport const SUB_AGENTS_DIR_NAME = \"sub-agents\";\nexport const SUB_AGENTS_DIR = join(WORKSPACE_DIR, SUB_AGENTS_DIR_NAME);\nexport const CHANNEL_CONFIG_PATH = join(APP_HOME_DIR, \"channel.json\");\nexport const AUTH_CONFIG_PATH = join(APP_HOME_DIR, \"auth.json\");\nexport const MODELS_CONFIG_PATH = join(APP_HOME_DIR, \"models.json\");\nexport const SETTINGS_CONFIG_PATH = join(APP_HOME_DIR, \"settings.json\");\n"]}
|
package/dist/paths.js
CHANGED
|
@@ -3,6 +3,8 @@ import { join } from "path";
|
|
|
3
3
|
export const APP_NAME = "pipiclaw";
|
|
4
4
|
export const APP_HOME_DIR = join(homedir(), ".pi", APP_NAME);
|
|
5
5
|
export const WORKSPACE_DIR = join(APP_HOME_DIR, "workspace");
|
|
6
|
+
export const SUB_AGENTS_DIR_NAME = "sub-agents";
|
|
7
|
+
export const SUB_AGENTS_DIR = join(WORKSPACE_DIR, SUB_AGENTS_DIR_NAME);
|
|
6
8
|
export const CHANNEL_CONFIG_PATH = join(APP_HOME_DIR, "channel.json");
|
|
7
9
|
export const AUTH_CONFIG_PATH = join(APP_HOME_DIR, "auth.json");
|
|
8
10
|
export const MODELS_CONFIG_PATH = join(APP_HOME_DIR, "models.json");
|
package/dist/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAAC;AACnC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAC7D,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC","sourcesContent":["import { homedir } from \"os\";\nimport { join } from \"path\";\n\nexport const APP_NAME = \"pipiclaw\";\nexport const APP_HOME_DIR = join(homedir(), \".pi\", APP_NAME);\nexport const WORKSPACE_DIR = join(APP_HOME_DIR, \"workspace\");\nexport const CHANNEL_CONFIG_PATH = join(APP_HOME_DIR, \"channel.json\");\nexport const AUTH_CONFIG_PATH = join(APP_HOME_DIR, \"auth.json\");\nexport const MODELS_CONFIG_PATH = join(APP_HOME_DIR, \"models.json\");\nexport const SETTINGS_CONFIG_PATH = join(APP_HOME_DIR, \"settings.json\");\n"]}
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAAC;AACnC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAC7D,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,YAAY,CAAC;AAChD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;AACvE,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC","sourcesContent":["import { homedir } from \"os\";\nimport { join } from \"path\";\n\nexport const APP_NAME = \"pipiclaw\";\nexport const APP_HOME_DIR = join(homedir(), \".pi\", APP_NAME);\nexport const WORKSPACE_DIR = join(APP_HOME_DIR, \"workspace\");\nexport const SUB_AGENTS_DIR_NAME = \"sub-agents\";\nexport const SUB_AGENTS_DIR = join(WORKSPACE_DIR, SUB_AGENTS_DIR_NAME);\nexport const CHANNEL_CONFIG_PATH = join(APP_HOME_DIR, \"channel.json\");\nexport const AUTH_CONFIG_PATH = join(APP_HOME_DIR, \"auth.json\");\nexport const MODELS_CONFIG_PATH = join(APP_HOME_DIR, \"models.json\");\nexport const SETTINGS_CONFIG_PATH = join(APP_HOME_DIR, \"settings.json\");\n"]}
|
package/dist/prompt-builder.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import type { SandboxConfig } from "./sandbox.js";
|
|
2
|
-
export
|
|
2
|
+
export interface AppendSystemPromptOptions {
|
|
3
|
+
subAgentList?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function buildAppendSystemPrompt(workspacePath: string, channelId: string, sandboxConfig: SandboxConfig, options?: AppendSystemPromptOptions): string;
|
|
3
6
|
//# sourceMappingURL=prompt-builder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-builder.d.ts","sourceRoot":"","sources":["../src/prompt-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,wBAAgB,uBAAuB,CACtC,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"prompt-builder.d.ts","sourceRoot":"","sources":["../src/prompt-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,yBAAyB;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,uBAAuB,CACtC,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,aAAa,EAC5B,OAAO,GAAE,yBAA8B,GACrC,MAAM,CAmJR","sourcesContent":["import type { SandboxConfig } from \"./sandbox.js\";\n\nexport interface AppendSystemPromptOptions {\n\tsubAgentList?: string;\n}\n\nexport function buildAppendSystemPrompt(\n\tworkspacePath: string,\n\tchannelId: string,\n\tsandboxConfig: SandboxConfig,\n\toptions: AppendSystemPromptOptions = {},\n): string {\n\tconst channelPath = `${workspacePath}/${channelId}`;\n\tconst subAgentsPath = `${workspacePath}/sub-agents`;\n\tconst isDocker = sandboxConfig.type === \"docker\";\n\n\tconst envDescription = isDocker\n\t\t? `You are running inside a Docker container (Alpine Linux).\n- Bash working directory: / (use cd or absolute paths)\n- Install tools with: apk add <package>\n- Your changes persist across sessions`\n\t\t: `You are running directly on the host machine.\n- Bash working directory: ${process.cwd()}\n- Be careful with system modifications`;\n\n\tconst sections: string[] = [];\n\n\tsections.push(`## Pipiclaw Runtime\nYou are running inside Pipiclaw, a DingTalk-oriented runtime built on top of pi.\n\n## Context\n- For current date/time, use: date\n- You have access to the active session context for this session.\n- Raw transcript files are cold storage. Do not assume they are preloaded.\n\n## Formatting\nUse Markdown for formatting. DingTalk AI Card supports basic Markdown:\nBold: **text**, Italic: *text*, Code: \\`code\\`, Block: \\`\\`\\`code\\`\\`\\`, Links: [text](url)\n\n## Environment\n${envDescription}\n\n## Workspace Layout\n${workspacePath}/\n├── SOUL.md # Your identity/personality (read-only)\n├── AGENTS.md # Custom behavior instructions (read-only)\n├── MEMORY.md # Stable workspace memory (admin-managed, read on demand)\n├── sub-agents/ # Predefined sub-agent definitions\n├── skills/ # Global CLI tools you create\n├── events/ # Scheduled events\n└── ${channelId}/ # This channel\n ├── MEMORY.md # Channel durable memory (read on demand, runtime-managed)\n ├── HISTORY.md # Channel summarized history (read on demand, runtime-managed)\n ├── log.jsonl # Raw message archive (cold storage)\n ├── context.jsonl # Raw session archive (cold storage)\n ├── scratch/ # Your working directory\n └── skills/ # Channel-specific tools`);\n\n\tsections.push(`## Events\nYou can schedule events that wake you up at specific times or when external things happen. Events are JSON files in \\`${workspacePath}/events/\\`.\n\n### Event Types\n\n**Immediate** - Triggers as soon as harness sees the file.\n\\`\\`\\`json\n{\"type\": \"immediate\", \"channelId\": \"${channelId}\", \"text\": \"New event occurred\"}\n\\`\\`\\`\n\n**One-shot** - Triggers once at a specific time.\n\\`\\`\\`json\n{\"type\": \"one-shot\", \"channelId\": \"${channelId}\", \"text\": \"Reminder\", \"at\": \"2025-12-15T09:00:00+08:00\"}\n\\`\\`\\`\n\n**Periodic** - Triggers on a cron schedule.\n\\`\\`\\`json\n{\"type\": \"periodic\", \"channelId\": \"${channelId}\", \"text\": \"Check inbox\", \"schedule\": \"0 9 * * 1-5\", \"timezone\": \"${Intl.DateTimeFormat().resolvedOptions().timeZone}\"}\n\\`\\`\\`\n\n### Cron Format\n\\`minute hour day-of-month month day-of-week\\`\n\n### Creating Events\nCreate a JSON file under \\`${workspacePath}/events/\\` with the appropriate event payload.\nPrefer the file tools for creating or editing the event file. Use shell commands only when they are the clearest option.\n\n### Silent Completion\nFor periodic events where there's nothing to report, respond with just \\`[SILENT]\\`. This deletes the status message. Use this to avoid spam when periodic checks find nothing.\n\n### Limits\nMaximum 5 events can be queued.`);\n\n\tsections.push(`## Memory\nMemory files are not preloaded into session context. Read them explicitly when memory or history matters.\n\n### Files\n- Workspace memory: ${workspacePath}/MEMORY.md\n Stable shared background memory. Admin-managed. Read on demand.\n- Channel memory: ${channelPath}/MEMORY.md\n Durable channel memory. Runtime-managed via consolidation. You may update this file manually when necessary.\n- Channel history: ${channelPath}/HISTORY.md\n Summarized older channel history. Runtime-managed. Read on demand. Do not maintain this file manually during normal work.\n\n### Runtime Behavior\n- The runtime automatically consolidates channel MEMORY.md and HISTORY.md before compaction or session trimming.\n- Workspace MEMORY.md is not updated by normal runtime consolidation.\n\n### Cold Storage\n- ${channelPath}/log.jsonl is a raw archive. It is not normal memory and is not proactively loaded.\n- ${channelPath}/context.jsonl is a raw session archive. It is not normal memory and is not proactively loaded.\n\nWhen a task depends on prior decisions, preferences, or long-running work, read channel MEMORY.md and HISTORY.md first.`);\n\n\tsections.push(`## System Configuration Log\nMaintain ${workspacePath}/SYSTEM.md to log all environment modifications:\n- Installed packages (apk add, npm install, pip install)\n- Environment variables set\n- Config files modified\n- Skill dependencies installed\n\nUpdate this file whenever you modify the environment.`);\n\n\tsections.push(`## Tools\n- read: Read files\n- edit: Surgical file edits\n- write: Create or overwrite files when needed\n- bash: Run shell commands and external programs\n- subagent: Delegate a focused task to a sub-agent with its own isolated context\n\nEach tool requires a \"label\" parameter (shown to user).`);\n\n\tsections.push(`## Sub-Agents\nYou have a \\`subagent\\` tool for delegating focused work to a separate agent with an isolated context window.\n\n### Predefined Sub-Agents\nPredefined sub-agent definitions live in \\`${subAgentsPath}/\\`.\n${options.subAgentList ? `Available predefined sub-agents:\\n${options.subAgentList}` : \"Available predefined sub-agents: none\"}\n\n### Temporary Inline Sub-Agents\nIf no predefined sub-agent fits, you may define a temporary inline sub-agent directly in the \\`subagent\\` tool call by providing a focused \\`systemPrompt\\` plus optional tools, model, and budget settings.\n\nUse sub-agents when:\n- The task can be decomposed into a focused sub-problem\n- You need a fresh context for heavy file reading, shell work, or review\n- A specialized role would produce better results\n- The main conversation has grown long and you want to offload a bounded task\n\nDo not use sub-agents when:\n- The task is simple and direct\n- The task depends heavily on the full current conversation state\n- The task requires frequent user confirmation\n\nImportant rules:\n- Sub-agents cannot see your conversation history unless you include the needed context in \\`task\\`\n- Sub-agents do not receive the \\`subagent\\` tool, so they cannot create nested agents\n- Prefer predefined sub-agents when one clearly fits\n- Use temporary inline sub-agents only when that extra flexibility is genuinely useful`);\n\n\treturn sections.join(\"\\n\\n\");\n}\n"]}
|
package/dist/prompt-builder.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export function buildAppendSystemPrompt(workspacePath, channelId, sandboxConfig) {
|
|
1
|
+
export function buildAppendSystemPrompt(workspacePath, channelId, sandboxConfig, options = {}) {
|
|
2
2
|
const channelPath = `${workspacePath}/${channelId}`;
|
|
3
|
+
const subAgentsPath = `${workspacePath}/sub-agents`;
|
|
3
4
|
const isDocker = sandboxConfig.type === "docker";
|
|
4
5
|
const envDescription = isDocker
|
|
5
6
|
? `You are running inside a Docker container (Alpine Linux).
|
|
@@ -30,6 +31,7 @@ ${workspacePath}/
|
|
|
30
31
|
├── SOUL.md # Your identity/personality (read-only)
|
|
31
32
|
├── AGENTS.md # Custom behavior instructions (read-only)
|
|
32
33
|
├── MEMORY.md # Stable workspace memory (admin-managed, read on demand)
|
|
34
|
+
├── sub-agents/ # Predefined sub-agent definitions
|
|
33
35
|
├── skills/ # Global CLI tools you create
|
|
34
36
|
├── events/ # Scheduled events
|
|
35
37
|
└── ${channelId}/ # This channel
|
|
@@ -104,8 +106,35 @@ Update this file whenever you modify the environment.`);
|
|
|
104
106
|
- edit: Surgical file edits
|
|
105
107
|
- write: Create or overwrite files when needed
|
|
106
108
|
- bash: Run shell commands and external programs
|
|
109
|
+
- subagent: Delegate a focused task to a sub-agent with its own isolated context
|
|
107
110
|
|
|
108
111
|
Each tool requires a "label" parameter (shown to user).`);
|
|
112
|
+
sections.push(`## Sub-Agents
|
|
113
|
+
You have a \`subagent\` tool for delegating focused work to a separate agent with an isolated context window.
|
|
114
|
+
|
|
115
|
+
### Predefined Sub-Agents
|
|
116
|
+
Predefined sub-agent definitions live in \`${subAgentsPath}/\`.
|
|
117
|
+
${options.subAgentList ? `Available predefined sub-agents:\n${options.subAgentList}` : "Available predefined sub-agents: none"}
|
|
118
|
+
|
|
119
|
+
### Temporary Inline Sub-Agents
|
|
120
|
+
If no predefined sub-agent fits, you may define a temporary inline sub-agent directly in the \`subagent\` tool call by providing a focused \`systemPrompt\` plus optional tools, model, and budget settings.
|
|
121
|
+
|
|
122
|
+
Use sub-agents when:
|
|
123
|
+
- The task can be decomposed into a focused sub-problem
|
|
124
|
+
- You need a fresh context for heavy file reading, shell work, or review
|
|
125
|
+
- A specialized role would produce better results
|
|
126
|
+
- The main conversation has grown long and you want to offload a bounded task
|
|
127
|
+
|
|
128
|
+
Do not use sub-agents when:
|
|
129
|
+
- The task is simple and direct
|
|
130
|
+
- The task depends heavily on the full current conversation state
|
|
131
|
+
- The task requires frequent user confirmation
|
|
132
|
+
|
|
133
|
+
Important rules:
|
|
134
|
+
- Sub-agents cannot see your conversation history unless you include the needed context in \`task\`
|
|
135
|
+
- Sub-agents do not receive the \`subagent\` tool, so they cannot create nested agents
|
|
136
|
+
- Prefer predefined sub-agents when one clearly fits
|
|
137
|
+
- Use temporary inline sub-agents only when that extra flexibility is genuinely useful`);
|
|
109
138
|
return sections.join("\n\n");
|
|
110
139
|
}
|
|
111
140
|
//# sourceMappingURL=prompt-builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-builder.js","sourceRoot":"","sources":["../src/prompt-builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prompt-builder.js","sourceRoot":"","sources":["../src/prompt-builder.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,uBAAuB,CACtC,aAAqB,EACrB,SAAiB,EACjB,aAA4B,EAC5B,OAAO,GAA8B,EAAE,EAC9B;IACT,MAAM,WAAW,GAAG,GAAG,aAAa,IAAI,SAAS,EAAE,CAAC;IACpD,MAAM,aAAa,GAAG,GAAG,aAAa,aAAa,CAAC;IACpD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,KAAK,QAAQ,CAAC;IAEjD,MAAM,cAAc,GAAG,QAAQ;QAC9B,CAAC,CAAC;;;uCAGmC;QACrC,CAAC,CAAC;4BACwB,OAAO,CAAC,GAAG,EAAE;uCACF,CAAC;IAEvC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,QAAQ,CAAC,IAAI,CAAC;;;;;;;;;;;;;EAab,cAAc;;;EAGd,aAAa;;;;;;;YAOT,SAAS;;;;;;gEAM2C,CAAC,CAAC;IAE3D,QAAQ,CAAC,IAAI,CAAC;wHACyG,aAAa;;;;;;sCAM/F,SAAS;;;;;qCAKV,SAAS;;;;;qCAKT,SAAS,qEAAqE,IAAI,CAAC,cAAc,EAAE,CAAC,eAAe,EAAE,CAAC,QAAQ;;;;;;;6BAOtI,aAAa;;;;;;;gCAOV,CAAC,CAAC;IAEjC,QAAQ,CAAC,IAAI,CAAC;;;;sBAIO,aAAa;;oBAEf,WAAW;;qBAEV,WAAW;;;;;;;;IAQ5B,WAAW;IACX,WAAW;;wHAEyG,CAAC,CAAC;IAEzH,QAAQ,CAAC,IAAI,CAAC;WACJ,aAAa;;;;;;sDAM8B,CAAC,CAAC;IAEvD,QAAQ,CAAC,IAAI,CAAC;;;;;;;wDAOyC,CAAC,CAAC;IAEzD,QAAQ,CAAC,IAAI,CAAC;;;;6CAI8B,aAAa;EACxD,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,qCAAqC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,uCAAuC;;;;;;;;;;;;;;;;;;;;uFAoBvC,CAAC,CAAC;IAExF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAAA,CAC7B","sourcesContent":["import type { SandboxConfig } from \"./sandbox.js\";\n\nexport interface AppendSystemPromptOptions {\n\tsubAgentList?: string;\n}\n\nexport function buildAppendSystemPrompt(\n\tworkspacePath: string,\n\tchannelId: string,\n\tsandboxConfig: SandboxConfig,\n\toptions: AppendSystemPromptOptions = {},\n): string {\n\tconst channelPath = `${workspacePath}/${channelId}`;\n\tconst subAgentsPath = `${workspacePath}/sub-agents`;\n\tconst isDocker = sandboxConfig.type === \"docker\";\n\n\tconst envDescription = isDocker\n\t\t? `You are running inside a Docker container (Alpine Linux).\n- Bash working directory: / (use cd or absolute paths)\n- Install tools with: apk add <package>\n- Your changes persist across sessions`\n\t\t: `You are running directly on the host machine.\n- Bash working directory: ${process.cwd()}\n- Be careful with system modifications`;\n\n\tconst sections: string[] = [];\n\n\tsections.push(`## Pipiclaw Runtime\nYou are running inside Pipiclaw, a DingTalk-oriented runtime built on top of pi.\n\n## Context\n- For current date/time, use: date\n- You have access to the active session context for this session.\n- Raw transcript files are cold storage. Do not assume they are preloaded.\n\n## Formatting\nUse Markdown for formatting. DingTalk AI Card supports basic Markdown:\nBold: **text**, Italic: *text*, Code: \\`code\\`, Block: \\`\\`\\`code\\`\\`\\`, Links: [text](url)\n\n## Environment\n${envDescription}\n\n## Workspace Layout\n${workspacePath}/\n├── SOUL.md # Your identity/personality (read-only)\n├── AGENTS.md # Custom behavior instructions (read-only)\n├── MEMORY.md # Stable workspace memory (admin-managed, read on demand)\n├── sub-agents/ # Predefined sub-agent definitions\n├── skills/ # Global CLI tools you create\n├── events/ # Scheduled events\n└── ${channelId}/ # This channel\n ├── MEMORY.md # Channel durable memory (read on demand, runtime-managed)\n ├── HISTORY.md # Channel summarized history (read on demand, runtime-managed)\n ├── log.jsonl # Raw message archive (cold storage)\n ├── context.jsonl # Raw session archive (cold storage)\n ├── scratch/ # Your working directory\n └── skills/ # Channel-specific tools`);\n\n\tsections.push(`## Events\nYou can schedule events that wake you up at specific times or when external things happen. Events are JSON files in \\`${workspacePath}/events/\\`.\n\n### Event Types\n\n**Immediate** - Triggers as soon as harness sees the file.\n\\`\\`\\`json\n{\"type\": \"immediate\", \"channelId\": \"${channelId}\", \"text\": \"New event occurred\"}\n\\`\\`\\`\n\n**One-shot** - Triggers once at a specific time.\n\\`\\`\\`json\n{\"type\": \"one-shot\", \"channelId\": \"${channelId}\", \"text\": \"Reminder\", \"at\": \"2025-12-15T09:00:00+08:00\"}\n\\`\\`\\`\n\n**Periodic** - Triggers on a cron schedule.\n\\`\\`\\`json\n{\"type\": \"periodic\", \"channelId\": \"${channelId}\", \"text\": \"Check inbox\", \"schedule\": \"0 9 * * 1-5\", \"timezone\": \"${Intl.DateTimeFormat().resolvedOptions().timeZone}\"}\n\\`\\`\\`\n\n### Cron Format\n\\`minute hour day-of-month month day-of-week\\`\n\n### Creating Events\nCreate a JSON file under \\`${workspacePath}/events/\\` with the appropriate event payload.\nPrefer the file tools for creating or editing the event file. Use shell commands only when they are the clearest option.\n\n### Silent Completion\nFor periodic events where there's nothing to report, respond with just \\`[SILENT]\\`. This deletes the status message. Use this to avoid spam when periodic checks find nothing.\n\n### Limits\nMaximum 5 events can be queued.`);\n\n\tsections.push(`## Memory\nMemory files are not preloaded into session context. Read them explicitly when memory or history matters.\n\n### Files\n- Workspace memory: ${workspacePath}/MEMORY.md\n Stable shared background memory. Admin-managed. Read on demand.\n- Channel memory: ${channelPath}/MEMORY.md\n Durable channel memory. Runtime-managed via consolidation. You may update this file manually when necessary.\n- Channel history: ${channelPath}/HISTORY.md\n Summarized older channel history. Runtime-managed. Read on demand. Do not maintain this file manually during normal work.\n\n### Runtime Behavior\n- The runtime automatically consolidates channel MEMORY.md and HISTORY.md before compaction or session trimming.\n- Workspace MEMORY.md is not updated by normal runtime consolidation.\n\n### Cold Storage\n- ${channelPath}/log.jsonl is a raw archive. It is not normal memory and is not proactively loaded.\n- ${channelPath}/context.jsonl is a raw session archive. It is not normal memory and is not proactively loaded.\n\nWhen a task depends on prior decisions, preferences, or long-running work, read channel MEMORY.md and HISTORY.md first.`);\n\n\tsections.push(`## System Configuration Log\nMaintain ${workspacePath}/SYSTEM.md to log all environment modifications:\n- Installed packages (apk add, npm install, pip install)\n- Environment variables set\n- Config files modified\n- Skill dependencies installed\n\nUpdate this file whenever you modify the environment.`);\n\n\tsections.push(`## Tools\n- read: Read files\n- edit: Surgical file edits\n- write: Create or overwrite files when needed\n- bash: Run shell commands and external programs\n- subagent: Delegate a focused task to a sub-agent with its own isolated context\n\nEach tool requires a \"label\" parameter (shown to user).`);\n\n\tsections.push(`## Sub-Agents\nYou have a \\`subagent\\` tool for delegating focused work to a separate agent with an isolated context window.\n\n### Predefined Sub-Agents\nPredefined sub-agent definitions live in \\`${subAgentsPath}/\\`.\n${options.subAgentList ? `Available predefined sub-agents:\\n${options.subAgentList}` : \"Available predefined sub-agents: none\"}\n\n### Temporary Inline Sub-Agents\nIf no predefined sub-agent fits, you may define a temporary inline sub-agent directly in the \\`subagent\\` tool call by providing a focused \\`systemPrompt\\` plus optional tools, model, and budget settings.\n\nUse sub-agents when:\n- The task can be decomposed into a focused sub-problem\n- You need a fresh context for heavy file reading, shell work, or review\n- A specialized role would produce better results\n- The main conversation has grown long and you want to offload a bounded task\n\nDo not use sub-agents when:\n- The task is simple and direct\n- The task depends heavily on the full current conversation state\n- The task requires frequent user confirmation\n\nImportant rules:\n- Sub-agents cannot see your conversation history unless you include the needed context in \\`task\\`\n- Sub-agents do not receive the \\`subagent\\` tool, so they cannot create nested agents\n- Prefer predefined sub-agents when one clearly fits\n- Use temporary inline sub-agents only when that extra flexibility is genuinely useful`);\n\n\treturn sections.join(\"\\n\\n\");\n}\n"]}
|
package/dist/sandbox.d.ts
CHANGED
package/dist/sandbox.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAc5D;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B1E;AAoBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,QAAQ,CAK9D;AAED,MAAM,WAAW,QAAQ;IACxB;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAElE;;;;OAIG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAc5D;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B1E;AAoBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,QAAQ,CAK9D;AAED,MAAM,WAAW,QAAQ;IACxB;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAElE;;;;OAIG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb","sourcesContent":["import { spawn } from \"child_process\";\nimport { shellEscape } from \"./shell-escape.js\";\n\nexport type SandboxConfig = { type: \"host\" } | { type: \"docker\"; container: string };\n\nexport function parseSandboxArg(value: string): SandboxConfig {\n\tif (value === \"host\") {\n\t\treturn { type: \"host\" };\n\t}\n\tif (value.startsWith(\"docker:\")) {\n\t\tconst container = value.slice(\"docker:\".length);\n\t\tif (!container) {\n\t\t\tconsole.error(\"Error: docker sandbox requires container name (e.g., docker:pipiclaw-sandbox)\");\n\t\t\tprocess.exit(1);\n\t\t}\n\t\treturn { type: \"docker\", container };\n\t}\n\tconsole.error(`Error: Invalid sandbox type '${value}'. Use 'host' or 'docker:<container-name>'`);\n\tprocess.exit(1);\n}\n\nexport async function validateSandbox(config: SandboxConfig): Promise<void> {\n\tif (config.type === \"host\") {\n\t\treturn;\n\t}\n\n\t// Check if Docker is available\n\ttry {\n\t\tawait execSimple(\"docker\", [\"--version\"]);\n\t} catch {\n\t\tconsole.error(\"Error: Docker is not installed or not in PATH\");\n\t\tprocess.exit(1);\n\t}\n\n\t// Check if container exists and is running\n\ttry {\n\t\tconst result = await execSimple(\"docker\", [\"inspect\", \"-f\", \"{{.State.Running}}\", config.container]);\n\t\tif (result.trim() !== \"true\") {\n\t\t\tconsole.error(`Error: Container '${config.container}' is not running.`);\n\t\t\tconsole.error(`Start it with: docker start ${config.container}`);\n\t\t\tprocess.exit(1);\n\t\t}\n\t} catch {\n\t\tconsole.error(`Error: Container '${config.container}' does not exist.`);\n\t\tconsole.error(\"Create it with: ./docker.sh create <data-dir>\");\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(` Docker container '${config.container}' is running.`);\n}\n\nfunction execSimple(cmd: string, args: string[]): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn(cmd, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tchild.stdout?.on(\"data\", (d) => {\n\t\t\tstdout += d;\n\t\t});\n\t\tchild.stderr?.on(\"data\", (d) => {\n\t\t\tstderr += d;\n\t\t});\n\t\tchild.on(\"close\", (code) => {\n\t\t\tif (code === 0) resolve(stdout);\n\t\t\telse reject(new Error(stderr || `Exit code ${code}`));\n\t\t});\n\t});\n}\n\n/**\n * Create an executor that runs commands either on host or in Docker container\n */\nexport function createExecutor(config: SandboxConfig): Executor {\n\tif (config.type === \"host\") {\n\t\treturn new HostExecutor();\n\t}\n\treturn new DockerExecutor(config.container);\n}\n\nexport interface Executor {\n\t/**\n\t * Execute a bash command\n\t */\n\texec(command: string, options?: ExecOptions): Promise<ExecResult>;\n\n\t/**\n\t * Get the workspace path prefix for this executor\n\t * Host: returns the actual path\n\t * Docker: returns /workspace\n\t */\n\tgetWorkspacePath(hostPath: string): string;\n}\n\nexport interface ExecOptions {\n\ttimeout?: number;\n\tsignal?: AbortSignal;\n\tstdin?: string;\n}\n\nexport interface ExecResult {\n\tstdout: string;\n\tstderr: string;\n\tcode: number;\n}\n\nclass HostExecutor implements Executor {\n\tasync exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst shell = process.platform === \"win32\" ? \"cmd\" : \"sh\";\n\t\t\tconst shellArgs = process.platform === \"win32\" ? [\"/c\"] : [\"-c\"];\n\t\t\tconst child = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn spawn(shell, [...shellArgs, command], {\n\t\t\t\t\t\tdetached: true,\n\t\t\t\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t\t\t\t});\n\t\t\t\t} catch (err) {\n\t\t\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\tif (!child) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet stdout = \"\";\n\t\t\tlet stderr = \"\";\n\t\t\tlet timedOut = false;\n\t\t\tlet settled = false;\n\n\t\t\tconst cleanup = () => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (options?.signal) {\n\t\t\t\t\toptions.signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst rejectOnce = (err: Error) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\treject(err);\n\t\t\t};\n\n\t\t\tconst resolveOnce = (result: ExecResult) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\tresolve(result);\n\t\t\t};\n\n\t\t\tconst timeoutHandle =\n\t\t\t\toptions?.timeout && options.timeout > 0\n\t\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\t\tkillProcessTree(child.pid!);\n\t\t\t\t\t\t}, options.timeout * 1000)\n\t\t\t\t\t: undefined;\n\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t};\n\n\t\t\tif (options?.signal) {\n\t\t\t\tif (options.signal.aborted) {\n\t\t\t\t\tonAbort();\n\t\t\t\t} else {\n\t\t\t\t\toptions.signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tchild.stdout?.on(\"data\", (data) => {\n\t\t\t\tstdout += data.toString();\n\t\t\t\tif (stdout.length > 10 * 1024 * 1024) {\n\t\t\t\t\tstdout = stdout.slice(0, 10 * 1024 * 1024);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (data) => {\n\t\t\t\tstderr += data.toString();\n\t\t\t\tif (stderr.length > 10 * 1024 * 1024) {\n\t\t\t\t\tstderr = stderr.slice(0, 10 * 1024 * 1024);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\trejectOnce(err instanceof Error ? err : new Error(String(err)));\n\t\t\t});\n\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\trejectOnce(new Error(`${stdout}\\n${stderr}\\nCommand aborted`.trim()));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (timedOut) {\n\t\t\t\t\trejectOnce(\n\t\t\t\t\t\tnew Error(`${stdout}\\n${stderr}\\nCommand timed out after ${options?.timeout} seconds`.trim()),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresolveOnce({ stdout, stderr, code: code ?? 0 });\n\t\t\t});\n\n\t\t\tif (options?.stdin !== undefined) {\n\t\t\t\tchild.stdin?.on(\"error\", (err) => {\n\t\t\t\t\tif ((err as NodeJS.ErrnoException).code === \"EPIPE\") return;\n\t\t\t\t\trejectOnce(err instanceof Error ? err : new Error(String(err)));\n\t\t\t\t});\n\t\t\t\tchild.stdin?.end(options.stdin);\n\t\t\t} else {\n\t\t\t\tchild.stdin?.end();\n\t\t\t}\n\t\t});\n\t}\n\n\tgetWorkspacePath(hostPath: string): string {\n\t\treturn hostPath;\n\t}\n}\n\nclass DockerExecutor implements Executor {\n\tconstructor(private container: string) {}\n\n\tasync exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n\t\t// Wrap command for docker exec\n\t\tconst interactive = options?.stdin !== undefined ? \"-i \" : \"\";\n\t\tconst dockerCmd = `docker exec ${interactive}${this.container} sh -c ${shellEscape(command)}`;\n\t\tconst hostExecutor = new HostExecutor();\n\t\treturn hostExecutor.exec(dockerCmd, options);\n\t}\n\n\tgetWorkspacePath(_hostPath: string): string {\n\t\t// Docker container sees /workspace\n\t\treturn \"/workspace\";\n\t}\n}\n\nfunction killProcessTree(pid: number): void {\n\tif (process.platform === \"win32\") {\n\t\ttry {\n\t\t\tspawn(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n\t\t\t\tstdio: \"ignore\",\n\t\t\t\tdetached: true,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Ignore errors\n\t\t}\n\t} else {\n\t\ttry {\n\t\t\tprocess.kill(-pid, \"SIGKILL\");\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tprocess.kill(pid, \"SIGKILL\");\n\t\t\t} catch {\n\t\t\t\t// Process already dead\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|