@oyasmi/pipiclaw 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -5
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +156 -57
- package/dist/agent.js.map +1 -1
- package/dist/context.d.ts +18 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +26 -0
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/llm-json.d.ts +7 -0
- package/dist/llm-json.d.ts.map +1 -0
- package/dist/llm-json.js +77 -0
- package/dist/llm-json.js.map +1 -0
- package/dist/markdown-sections.d.ts +6 -0
- package/dist/markdown-sections.d.ts.map +1 -0
- package/dist/markdown-sections.js +34 -0
- package/dist/markdown-sections.js.map +1 -0
- package/dist/memory-candidates.d.ts +21 -0
- package/dist/memory-candidates.d.ts.map +1 -0
- package/dist/memory-candidates.js +126 -0
- package/dist/memory-candidates.js.map +1 -0
- package/dist/memory-consolidation.d.ts.map +1 -1
- package/dist/memory-consolidation.js +28 -49
- package/dist/memory-consolidation.js.map +1 -1
- package/dist/memory-files.d.ts +3 -0
- package/dist/memory-files.d.ts.map +1 -1
- package/dist/memory-files.js +51 -0
- package/dist/memory-files.js.map +1 -1
- package/dist/memory-lifecycle.d.ts +9 -0
- package/dist/memory-lifecycle.d.ts.map +1 -1
- package/dist/memory-lifecycle.js +66 -0
- package/dist/memory-lifecycle.js.map +1 -1
- package/dist/memory-recall.d.ts +29 -0
- package/dist/memory-recall.d.ts.map +1 -0
- package/dist/memory-recall.js +218 -0
- package/dist/memory-recall.js.map +1 -0
- package/dist/prompt-builder.d.ts.map +1 -1
- package/dist/prompt-builder.js +7 -2
- package/dist/prompt-builder.js.map +1 -1
- package/dist/session-memory-files.d.ts +2 -0
- package/dist/session-memory-files.d.ts.map +1 -0
- package/dist/session-memory-files.js +2 -0
- package/dist/session-memory-files.js.map +1 -0
- package/dist/session-memory.d.ts +22 -0
- package/dist/session-memory.d.ts.map +1 -0
- package/dist/session-memory.js +274 -0
- package/dist/session-memory.js.map +1 -0
- package/dist/sidecar-worker.d.ts +27 -0
- package/dist/sidecar-worker.d.ts.map +1 -0
- package/dist/sidecar-worker.js +105 -0
- package/dist/sidecar-worker.js.map +1 -0
- package/dist/sub-agents.d.ts +10 -0
- package/dist/sub-agents.d.ts.map +1 -1
- package/dist/sub-agents.js +90 -0
- package/dist/sub-agents.js.map +1 -1
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/subagent.d.ts +6 -0
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +127 -12
- package/dist/tools/subagent.js.map +1 -1
- package/docs/improve-memory/design.md +537 -0
- package/docs/improve-memory/interfaces-and-tests.md +473 -0
- package/docs/improve-memory/spec.md +357 -0
- package/docs/memory-rfc.md +7 -1
- package/docs/proj-review.md +188 -0
- package/docs/test-supplementation-plan.md +553 -0
- package/package.json +3 -1
package/dist/prompt-builder.js
CHANGED
|
@@ -35,6 +35,7 @@ ${workspacePath}/
|
|
|
35
35
|
├── skills/ # Global CLI tools you create
|
|
36
36
|
├── events/ # Scheduled events
|
|
37
37
|
└── ${channelId}/ # This channel
|
|
38
|
+
├── SESSION.md # Channel working memory (runtime-managed, read on demand)
|
|
38
39
|
├── MEMORY.md # Channel durable memory (read on demand, runtime-managed)
|
|
39
40
|
├── HISTORY.md # Channel summarized history (read on demand, runtime-managed)
|
|
40
41
|
├── log.jsonl # Raw message archive (cold storage)
|
|
@@ -79,12 +80,16 @@ Memory files are not preloaded into session context. Read them explicitly when m
|
|
|
79
80
|
### Files
|
|
80
81
|
- Workspace memory: ${workspacePath}/MEMORY.md
|
|
81
82
|
Stable shared background memory. Admin-managed. Read on demand.
|
|
83
|
+
- Channel session memory: ${channelPath}/SESSION.md
|
|
84
|
+
Current working state for this channel. Runtime-managed. Read on demand. Prefer this when current task state matters.
|
|
82
85
|
- Channel memory: ${channelPath}/MEMORY.md
|
|
83
|
-
Durable channel memory. Runtime-managed via consolidation.
|
|
86
|
+
Durable channel memory. Runtime-managed via consolidation. Prefer this for stable facts, decisions, preferences, and medium-horizon open loops.
|
|
84
87
|
- Channel history: ${channelPath}/HISTORY.md
|
|
85
88
|
Summarized older channel history. Runtime-managed. Read on demand. Do not maintain this file manually during normal work.
|
|
86
89
|
|
|
87
90
|
### Runtime Behavior
|
|
91
|
+
- The runtime may inject a small amount of relevant memory context from SESSION.md / MEMORY.md / HISTORY.md into a turn when it is clearly useful.
|
|
92
|
+
- SESSION.md is the primary runtime-managed working-state artifact for current active work.
|
|
88
93
|
- The runtime automatically consolidates channel MEMORY.md and HISTORY.md before compaction or session trimming.
|
|
89
94
|
- Workspace MEMORY.md is not updated by normal runtime consolidation.
|
|
90
95
|
|
|
@@ -92,7 +97,7 @@ Memory files are not preloaded into session context. Read them explicitly when m
|
|
|
92
97
|
- ${channelPath}/log.jsonl is a raw archive. It is not normal memory and is not proactively loaded.
|
|
93
98
|
- ${channelPath}/context.jsonl is a raw session archive. It is not normal memory and is not proactively loaded.
|
|
94
99
|
|
|
95
|
-
When a task depends on prior decisions, preferences, or long-running work,
|
|
100
|
+
When a task depends on prior decisions, preferences, or long-running work, prefer SESSION.md first for current state, then MEMORY.md, then HISTORY.md.`);
|
|
96
101
|
sections.push(`## System Configuration Log
|
|
97
102
|
Maintain ${workspacePath}/SYSTEM.md to log all environment modifications:
|
|
98
103
|
- Installed packages (apk add, npm install, pip install)
|
|
@@ -1 +1 @@
|
|
|
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,UAAqC,EAAE;IAEvC,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;;;;;;;MAOT,SAAS
|
|
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,UAAqC,EAAE;IAEvC,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;;;;;;;MAOT,SAAS;;;;;;;0DAO2C,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;;4BAEP,WAAW;;oBAEnB,WAAW;;qBAEV,WAAW;;;;;;;;;;IAU5B,WAAW;IACX,WAAW;;uJAEwI,CAAC,CAAC;IAExJ,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;;;;;;;;;;;;;;;;;;;;;uFAqBvC,CAAC,CAAC;IAExF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC","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 ├── SESSION.md # Channel working memory (runtime-managed, read on demand)\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 session memory: ${channelPath}/SESSION.md\n Current working state for this channel. Runtime-managed. Read on demand. Prefer this when current task state matters.\n- Channel memory: ${channelPath}/MEMORY.md\n Durable channel memory. Runtime-managed via consolidation. Prefer this for stable facts, decisions, preferences, and medium-horizon open loops.\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 may inject a small amount of relevant memory context from SESSION.md / MEMORY.md / HISTORY.md into a turn when it is clearly useful.\n- SESSION.md is the primary runtime-managed working-state artifact for current active work.\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, prefer SESSION.md first for current state, then MEMORY.md, then HISTORY.md.`);\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- The runtime injects a small fixed execution context (workspace path, channel id, sandbox), but you must still include task-specific context yourself\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"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-memory-files.d.ts","sourceRoot":"","sources":["../src/session-memory-files.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,wBAAwB,EACxB,4BAA4B,EAC5B,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,GACrB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-memory-files.js","sourceRoot":"","sources":["../src/session-memory-files.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,wBAAwB,EACxB,4BAA4B,EAC5B,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,GACrB,MAAM,mBAAmB,CAAC","sourcesContent":["export {\n\tensureChannelMemoryFiles,\n\tensureChannelMemoryFilesSync,\n\tgetChannelSessionPath,\n\treadChannelSession,\n\trewriteChannelSession,\n} from \"./memory-files.js\";\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
|
2
|
+
import type { Api, Model } from "@mariozechner/pi-ai";
|
|
3
|
+
export interface SessionMemoryState {
|
|
4
|
+
title: string;
|
|
5
|
+
currentState: string[];
|
|
6
|
+
userIntent: string[];
|
|
7
|
+
activeFiles: string[];
|
|
8
|
+
decisions: string[];
|
|
9
|
+
constraints: string[];
|
|
10
|
+
errorsAndCorrections: string[];
|
|
11
|
+
nextSteps: string[];
|
|
12
|
+
worklog: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface SessionMemoryUpdateOptions {
|
|
15
|
+
channelDir: string;
|
|
16
|
+
messages: AgentMessage[];
|
|
17
|
+
model: Model<Api>;
|
|
18
|
+
resolveApiKey: (model: Model<Api>) => Promise<string>;
|
|
19
|
+
}
|
|
20
|
+
export declare function renderSessionMemory(state: SessionMemoryState): string;
|
|
21
|
+
export declare function updateChannelSessionMemory(options: SessionMemoryUpdateOptions): Promise<SessionMemoryState>;
|
|
22
|
+
//# sourceMappingURL=session-memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-memory.d.ts","sourceRoot":"","sources":["../src/session-memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,GAAG,EAAW,KAAK,EAAE,MAAM,qBAAqB,CAAC;AA2C/D,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,0BAA0B;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACtD;AA0LD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAyBrE;AAcD,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA4BjH"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { serializeConversation } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { writeFile } from "fs/promises";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { parseJsonObject } from "./llm-json.js";
|
|
5
|
+
import { splitLevelOneSections } from "./markdown-sections.js";
|
|
6
|
+
import { readChannelMemory } from "./memory-files.js";
|
|
7
|
+
import { readChannelSession, rewriteChannelSession } from "./session-memory-files.js";
|
|
8
|
+
import { runSidecarTask, SidecarParseError } from "./sidecar-worker.js";
|
|
9
|
+
const SESSION_TRANSCRIPT_MAX_CHARS = 20_000;
|
|
10
|
+
const SESSION_MEMORY_MAX_CHARS = 4_000;
|
|
11
|
+
const SESSION_ITEM_LIMIT = 12;
|
|
12
|
+
const SESSION_ITEM_MAX_CHARS = 300;
|
|
13
|
+
const SESSION_MEMORY_TIMEOUT_MS = 10_000;
|
|
14
|
+
const SESSION_MEMORY_SYSTEM_PROMPT = `You maintain a Pipiclaw SESSION.md file.
|
|
15
|
+
|
|
16
|
+
Return strict JSON only. Do not use Markdown fences.
|
|
17
|
+
|
|
18
|
+
Output schema:
|
|
19
|
+
{
|
|
20
|
+
"title": "string",
|
|
21
|
+
"currentState": ["string"],
|
|
22
|
+
"userIntent": ["string"],
|
|
23
|
+
"activeFiles": ["string"],
|
|
24
|
+
"decisions": ["string"],
|
|
25
|
+
"constraints": ["string"],
|
|
26
|
+
"errorsAndCorrections": ["string"],
|
|
27
|
+
"nextSteps": ["string"],
|
|
28
|
+
"worklog": ["string"]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
Rules:
|
|
32
|
+
- Prefer short, information-dense bullet-sized items.
|
|
33
|
+
- Capture only the current active work state, not the full conversation history.
|
|
34
|
+
- Keep durable facts out unless they are directly relevant to the current work.
|
|
35
|
+
- "activeFiles" should list only files, directories, or work areas currently in focus.
|
|
36
|
+
- "errorsAndCorrections" should record recent failures, fixes, or things to avoid repeating.
|
|
37
|
+
- "nextSteps" should reflect the most likely immediate follow-up actions.
|
|
38
|
+
- "worklog" must stay terse and recent.
|
|
39
|
+
- If a field has nothing useful, return an empty string or empty array.`;
|
|
40
|
+
function clipText(text, maxChars) {
|
|
41
|
+
const normalized = text.replace(/\r/g, "").trim();
|
|
42
|
+
if (normalized.length <= maxChars) {
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
const headChars = Math.floor(maxChars * 0.45);
|
|
46
|
+
const tailChars = maxChars - headChars;
|
|
47
|
+
return `${normalized.slice(0, headChars)}\n\n[... omitted middle section ...]\n\n${normalized.slice(-tailChars)}`;
|
|
48
|
+
}
|
|
49
|
+
function normalizeItem(value) {
|
|
50
|
+
if (typeof value !== "string") {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
54
|
+
if (!normalized) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return normalized.length > SESSION_ITEM_MAX_CHARS
|
|
58
|
+
? `${normalized.slice(0, SESSION_ITEM_MAX_CHARS - 3)}...`
|
|
59
|
+
: normalized;
|
|
60
|
+
}
|
|
61
|
+
function normalizeItems(value) {
|
|
62
|
+
if (!Array.isArray(value)) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
return value
|
|
66
|
+
.map(normalizeItem)
|
|
67
|
+
.filter((item) => !!item)
|
|
68
|
+
.slice(0, SESSION_ITEM_LIMIT);
|
|
69
|
+
}
|
|
70
|
+
function normalizeTitle(value) {
|
|
71
|
+
return typeof value === "string" ? value.trim().slice(0, 120) : "";
|
|
72
|
+
}
|
|
73
|
+
function isRecord(value) {
|
|
74
|
+
return typeof value === "object" && value !== null;
|
|
75
|
+
}
|
|
76
|
+
function parseStateUpdate(text) {
|
|
77
|
+
const parsed = parseJsonObject(text);
|
|
78
|
+
if (!isRecord(parsed)) {
|
|
79
|
+
throw new Error("Session memory response was not a JSON object");
|
|
80
|
+
}
|
|
81
|
+
const next = {};
|
|
82
|
+
if ("title" in parsed)
|
|
83
|
+
next.title = normalizeTitle(parsed.title);
|
|
84
|
+
if ("currentState" in parsed)
|
|
85
|
+
next.currentState = normalizeItems(parsed.currentState);
|
|
86
|
+
if ("userIntent" in parsed)
|
|
87
|
+
next.userIntent = normalizeItems(parsed.userIntent);
|
|
88
|
+
if ("activeFiles" in parsed)
|
|
89
|
+
next.activeFiles = normalizeItems(parsed.activeFiles);
|
|
90
|
+
if ("decisions" in parsed)
|
|
91
|
+
next.decisions = normalizeItems(parsed.decisions);
|
|
92
|
+
if ("constraints" in parsed)
|
|
93
|
+
next.constraints = normalizeItems(parsed.constraints);
|
|
94
|
+
if ("errorsAndCorrections" in parsed)
|
|
95
|
+
next.errorsAndCorrections = normalizeItems(parsed.errorsAndCorrections);
|
|
96
|
+
if ("nextSteps" in parsed)
|
|
97
|
+
next.nextSteps = normalizeItems(parsed.nextSteps);
|
|
98
|
+
if ("worklog" in parsed)
|
|
99
|
+
next.worklog = normalizeItems(parsed.worklog);
|
|
100
|
+
return next;
|
|
101
|
+
}
|
|
102
|
+
function createEmptySessionMemoryState() {
|
|
103
|
+
return {
|
|
104
|
+
title: "",
|
|
105
|
+
currentState: [],
|
|
106
|
+
userIntent: [],
|
|
107
|
+
activeFiles: [],
|
|
108
|
+
decisions: [],
|
|
109
|
+
constraints: [],
|
|
110
|
+
errorsAndCorrections: [],
|
|
111
|
+
nextSteps: [],
|
|
112
|
+
worklog: [],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function stripHtmlComments(text) {
|
|
116
|
+
return text.replace(/<!--[\s\S]*?-->/g, "").trim();
|
|
117
|
+
}
|
|
118
|
+
function parseSectionItems(content) {
|
|
119
|
+
const normalized = stripHtmlComments(content);
|
|
120
|
+
if (!normalized) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
const lines = normalized
|
|
124
|
+
.split("\n")
|
|
125
|
+
.map((line) => line.trim())
|
|
126
|
+
.filter(Boolean);
|
|
127
|
+
const bulletItems = lines
|
|
128
|
+
.filter((line) => line.startsWith("- "))
|
|
129
|
+
.map((line) => normalizeItem(line.slice(2)))
|
|
130
|
+
.filter((item) => !!item);
|
|
131
|
+
if (bulletItems.length > 0) {
|
|
132
|
+
return bulletItems.slice(0, SESSION_ITEM_LIMIT);
|
|
133
|
+
}
|
|
134
|
+
return lines
|
|
135
|
+
.map(normalizeItem)
|
|
136
|
+
.filter((item) => !!item)
|
|
137
|
+
.slice(0, SESSION_ITEM_LIMIT);
|
|
138
|
+
}
|
|
139
|
+
function parseRenderedSessionMemory(markdown) {
|
|
140
|
+
const state = createEmptySessionMemoryState();
|
|
141
|
+
for (const section of splitLevelOneSections(markdown)) {
|
|
142
|
+
switch (section.heading.toLowerCase()) {
|
|
143
|
+
case "session title":
|
|
144
|
+
state.title = stripHtmlComments(section.content).split("\n")[0]?.trim().slice(0, 120) || "";
|
|
145
|
+
break;
|
|
146
|
+
case "current state":
|
|
147
|
+
state.currentState = parseSectionItems(section.content);
|
|
148
|
+
break;
|
|
149
|
+
case "user intent":
|
|
150
|
+
state.userIntent = parseSectionItems(section.content);
|
|
151
|
+
break;
|
|
152
|
+
case "active files":
|
|
153
|
+
state.activeFiles = parseSectionItems(section.content);
|
|
154
|
+
break;
|
|
155
|
+
case "decisions":
|
|
156
|
+
state.decisions = parseSectionItems(section.content);
|
|
157
|
+
break;
|
|
158
|
+
case "constraints":
|
|
159
|
+
state.constraints = parseSectionItems(section.content);
|
|
160
|
+
break;
|
|
161
|
+
case "errors & corrections":
|
|
162
|
+
state.errorsAndCorrections = parseSectionItems(section.content);
|
|
163
|
+
break;
|
|
164
|
+
case "next steps":
|
|
165
|
+
state.nextSteps = parseSectionItems(section.content);
|
|
166
|
+
break;
|
|
167
|
+
case "worklog":
|
|
168
|
+
state.worklog = parseSectionItems(section.content);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return state;
|
|
173
|
+
}
|
|
174
|
+
function mergeSessionMemoryState(current, update) {
|
|
175
|
+
return {
|
|
176
|
+
title: typeof update.title === "string" ? update.title : current.title,
|
|
177
|
+
currentState: Array.isArray(update.currentState) ? update.currentState : current.currentState,
|
|
178
|
+
userIntent: Array.isArray(update.userIntent) ? update.userIntent : current.userIntent,
|
|
179
|
+
activeFiles: Array.isArray(update.activeFiles) ? update.activeFiles : current.activeFiles,
|
|
180
|
+
decisions: Array.isArray(update.decisions) ? update.decisions : current.decisions,
|
|
181
|
+
constraints: Array.isArray(update.constraints) ? update.constraints : current.constraints,
|
|
182
|
+
errorsAndCorrections: Array.isArray(update.errorsAndCorrections)
|
|
183
|
+
? update.errorsAndCorrections
|
|
184
|
+
: current.errorsAndCorrections,
|
|
185
|
+
nextSteps: Array.isArray(update.nextSteps) ? update.nextSteps : current.nextSteps,
|
|
186
|
+
worklog: Array.isArray(update.worklog) ? update.worklog : current.worklog,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
async function writeSessionMemoryDebugFile(channelDir, error, rawText) {
|
|
190
|
+
const debugPath = join(channelDir, "SESSION.invalid-response.txt");
|
|
191
|
+
const header = [
|
|
192
|
+
`timestamp: ${new Date().toISOString()}`,
|
|
193
|
+
`error: ${error instanceof Error ? error.message : String(error)}`,
|
|
194
|
+
"",
|
|
195
|
+
"raw response:",
|
|
196
|
+
"",
|
|
197
|
+
].join("\n");
|
|
198
|
+
await writeFile(debugPath, `${header}${rawText}\n`, "utf-8");
|
|
199
|
+
}
|
|
200
|
+
function renderSection(heading, items) {
|
|
201
|
+
return [`# ${heading}`, "", ...items.map((item) => `- ${item}`)].join("\n");
|
|
202
|
+
}
|
|
203
|
+
function isStandardAgentMessage(message) {
|
|
204
|
+
return (typeof message === "object" &&
|
|
205
|
+
message !== null &&
|
|
206
|
+
"role" in message &&
|
|
207
|
+
(message.role === "user" || message.role === "assistant" || message.role === "toolResult"));
|
|
208
|
+
}
|
|
209
|
+
function buildMessagesForSessionMemory(messages) {
|
|
210
|
+
return messages.filter(isStandardAgentMessage);
|
|
211
|
+
}
|
|
212
|
+
export function renderSessionMemory(state) {
|
|
213
|
+
const sections = [];
|
|
214
|
+
if (state.title.trim()) {
|
|
215
|
+
sections.push("# Session Title", "", state.title.trim());
|
|
216
|
+
}
|
|
217
|
+
const orderedSections = [
|
|
218
|
+
["Current State", state.currentState],
|
|
219
|
+
["User Intent", state.userIntent],
|
|
220
|
+
["Active Files", state.activeFiles],
|
|
221
|
+
["Decisions", state.decisions],
|
|
222
|
+
["Constraints", state.constraints],
|
|
223
|
+
["Errors & Corrections", state.errorsAndCorrections],
|
|
224
|
+
["Next Steps", state.nextSteps],
|
|
225
|
+
["Worklog", state.worklog],
|
|
226
|
+
];
|
|
227
|
+
for (const [heading, items] of orderedSections) {
|
|
228
|
+
if (items.length === 0 && heading === "Worklog") {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
sections.push(renderSection(heading, items));
|
|
232
|
+
}
|
|
233
|
+
return `${sections.join("\n\n").trim()}\n`;
|
|
234
|
+
}
|
|
235
|
+
function buildSessionPrompt(currentSession, currentMemory, messages) {
|
|
236
|
+
const transcript = clipText(serializeConversation(messages), SESSION_TRANSCRIPT_MAX_CHARS);
|
|
237
|
+
return `Current SESSION.md:
|
|
238
|
+
${currentSession || "(empty)"}
|
|
239
|
+
|
|
240
|
+
Current channel MEMORY.md:
|
|
241
|
+
${clipText(currentMemory, SESSION_MEMORY_MAX_CHARS) || "(empty)"}
|
|
242
|
+
|
|
243
|
+
Recent conversation:
|
|
244
|
+
${transcript || "(empty)"}`;
|
|
245
|
+
}
|
|
246
|
+
export async function updateChannelSessionMemory(options) {
|
|
247
|
+
const currentSession = await readChannelSession(options.channelDir);
|
|
248
|
+
const currentMemory = await readChannelMemory(options.channelDir);
|
|
249
|
+
const messages = buildMessagesForSessionMemory(options.messages);
|
|
250
|
+
const currentState = parseRenderedSessionMemory(currentSession);
|
|
251
|
+
let update;
|
|
252
|
+
try {
|
|
253
|
+
const result = await runSidecarTask({
|
|
254
|
+
name: "session-memory-update",
|
|
255
|
+
model: options.model,
|
|
256
|
+
resolveApiKey: options.resolveApiKey,
|
|
257
|
+
systemPrompt: SESSION_MEMORY_SYSTEM_PROMPT,
|
|
258
|
+
prompt: buildSessionPrompt(currentSession, currentMemory, messages),
|
|
259
|
+
parse: parseStateUpdate,
|
|
260
|
+
timeoutMs: SESSION_MEMORY_TIMEOUT_MS,
|
|
261
|
+
});
|
|
262
|
+
update = result.output;
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
if (error instanceof SidecarParseError) {
|
|
266
|
+
await writeSessionMemoryDebugFile(options.channelDir, error.cause ?? error, error.rawText);
|
|
267
|
+
}
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
const rendered = renderSessionMemory(mergeSessionMemoryState(currentState, update));
|
|
271
|
+
await rewriteChannelSession(options.channelDir, rendered);
|
|
272
|
+
return parseRenderedSessionMemory(rendered);
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=session-memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-memory.js","sourceRoot":"","sources":["../src/session-memory.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@mariozechner/pi-agent-core\";\nimport type { Api, Message, Model } from \"@mariozechner/pi-ai\";\nimport { serializeConversation } from \"@mariozechner/pi-coding-agent\";\nimport { writeFile } from \"fs/promises\";\nimport { join } from \"path\";\nimport { parseJsonObject } from \"./llm-json.js\";\nimport { splitLevelOneSections } from \"./markdown-sections.js\";\nimport { readChannelMemory } from \"./memory-files.js\";\nimport { readChannelSession, rewriteChannelSession } from \"./session-memory-files.js\";\nimport { runSidecarTask, SidecarParseError } from \"./sidecar-worker.js\";\n\nconst SESSION_TRANSCRIPT_MAX_CHARS = 20_000;\nconst SESSION_MEMORY_MAX_CHARS = 4_000;\nconst SESSION_ITEM_LIMIT = 12;\nconst SESSION_ITEM_MAX_CHARS = 300;\nconst SESSION_MEMORY_TIMEOUT_MS = 10_000;\n\nconst SESSION_MEMORY_SYSTEM_PROMPT = `You maintain a Pipiclaw SESSION.md file.\n\nReturn strict JSON only. Do not use Markdown fences.\n\nOutput schema:\n{\n \"title\": \"string\",\n \"currentState\": [\"string\"],\n \"userIntent\": [\"string\"],\n \"activeFiles\": [\"string\"],\n \"decisions\": [\"string\"],\n \"constraints\": [\"string\"],\n \"errorsAndCorrections\": [\"string\"],\n \"nextSteps\": [\"string\"],\n \"worklog\": [\"string\"]\n}\n\nRules:\n- Prefer short, information-dense bullet-sized items.\n- Capture only the current active work state, not the full conversation history.\n- Keep durable facts out unless they are directly relevant to the current work.\n- \"activeFiles\" should list only files, directories, or work areas currently in focus.\n- \"errorsAndCorrections\" should record recent failures, fixes, or things to avoid repeating.\n- \"nextSteps\" should reflect the most likely immediate follow-up actions.\n- \"worklog\" must stay terse and recent.\n- If a field has nothing useful, return an empty string or empty array.`;\n\nexport interface SessionMemoryState {\n\ttitle: string;\n\tcurrentState: string[];\n\tuserIntent: string[];\n\tactiveFiles: string[];\n\tdecisions: string[];\n\tconstraints: string[];\n\terrorsAndCorrections: string[];\n\tnextSteps: string[];\n\tworklog: string[];\n}\n\nexport interface SessionMemoryUpdateOptions {\n\tchannelDir: string;\n\tmessages: AgentMessage[];\n\tmodel: Model<Api>;\n\tresolveApiKey: (model: Model<Api>) => Promise<string>;\n}\n\ntype SessionMemoryStateUpdate = Partial<Record<keyof SessionMemoryState, string[] | string>>;\n\nfunction clipText(text: string, maxChars: number): string {\n\tconst normalized = text.replace(/\\r/g, \"\").trim();\n\tif (normalized.length <= maxChars) {\n\t\treturn normalized;\n\t}\n\tconst headChars = Math.floor(maxChars * 0.45);\n\tconst tailChars = maxChars - headChars;\n\treturn `${normalized.slice(0, headChars)}\\n\\n[... omitted middle section ...]\\n\\n${normalized.slice(-tailChars)}`;\n}\n\nfunction normalizeItem(value: unknown): string | null {\n\tif (typeof value !== \"string\") {\n\t\treturn null;\n\t}\n\tconst normalized = value.replace(/\\s+/g, \" \").trim();\n\tif (!normalized) {\n\t\treturn null;\n\t}\n\treturn normalized.length > SESSION_ITEM_MAX_CHARS\n\t\t? `${normalized.slice(0, SESSION_ITEM_MAX_CHARS - 3)}...`\n\t\t: normalized;\n}\n\nfunction normalizeItems(value: unknown): string[] {\n\tif (!Array.isArray(value)) {\n\t\treturn [];\n\t}\n\treturn value\n\t\t.map(normalizeItem)\n\t\t.filter((item): item is string => !!item)\n\t\t.slice(0, SESSION_ITEM_LIMIT);\n}\n\nfunction normalizeTitle(value: unknown): string {\n\treturn typeof value === \"string\" ? value.trim().slice(0, 120) : \"\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction parseStateUpdate(text: string): SessionMemoryStateUpdate {\n\tconst parsed = parseJsonObject(text);\n\tif (!isRecord(parsed)) {\n\t\tthrow new Error(\"Session memory response was not a JSON object\");\n\t}\n\n\tconst next: SessionMemoryStateUpdate = {};\n\tif (\"title\" in parsed) next.title = normalizeTitle(parsed.title);\n\tif (\"currentState\" in parsed) next.currentState = normalizeItems(parsed.currentState);\n\tif (\"userIntent\" in parsed) next.userIntent = normalizeItems(parsed.userIntent);\n\tif (\"activeFiles\" in parsed) next.activeFiles = normalizeItems(parsed.activeFiles);\n\tif (\"decisions\" in parsed) next.decisions = normalizeItems(parsed.decisions);\n\tif (\"constraints\" in parsed) next.constraints = normalizeItems(parsed.constraints);\n\tif (\"errorsAndCorrections\" in parsed) next.errorsAndCorrections = normalizeItems(parsed.errorsAndCorrections);\n\tif (\"nextSteps\" in parsed) next.nextSteps = normalizeItems(parsed.nextSteps);\n\tif (\"worklog\" in parsed) next.worklog = normalizeItems(parsed.worklog);\n\treturn next;\n}\n\nfunction createEmptySessionMemoryState(): SessionMemoryState {\n\treturn {\n\t\ttitle: \"\",\n\t\tcurrentState: [],\n\t\tuserIntent: [],\n\t\tactiveFiles: [],\n\t\tdecisions: [],\n\t\tconstraints: [],\n\t\terrorsAndCorrections: [],\n\t\tnextSteps: [],\n\t\tworklog: [],\n\t};\n}\n\nfunction stripHtmlComments(text: string): string {\n\treturn text.replace(/<!--[\\s\\S]*?-->/g, \"\").trim();\n}\n\nfunction parseSectionItems(content: string): string[] {\n\tconst normalized = stripHtmlComments(content);\n\tif (!normalized) {\n\t\treturn [];\n\t}\n\n\tconst lines = normalized\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean);\n\tconst bulletItems = lines\n\t\t.filter((line) => line.startsWith(\"- \"))\n\t\t.map((line) => normalizeItem(line.slice(2)))\n\t\t.filter((item): item is string => !!item);\n\tif (bulletItems.length > 0) {\n\t\treturn bulletItems.slice(0, SESSION_ITEM_LIMIT);\n\t}\n\treturn lines\n\t\t.map(normalizeItem)\n\t\t.filter((item): item is string => !!item)\n\t\t.slice(0, SESSION_ITEM_LIMIT);\n}\n\nfunction parseRenderedSessionMemory(markdown: string): SessionMemoryState {\n\tconst state = createEmptySessionMemoryState();\n\tfor (const section of splitLevelOneSections(markdown)) {\n\t\tswitch (section.heading.toLowerCase()) {\n\t\t\tcase \"session title\":\n\t\t\t\tstate.title = stripHtmlComments(section.content).split(\"\\n\")[0]?.trim().slice(0, 120) || \"\";\n\t\t\t\tbreak;\n\t\t\tcase \"current state\":\n\t\t\t\tstate.currentState = parseSectionItems(section.content);\n\t\t\t\tbreak;\n\t\t\tcase \"user intent\":\n\t\t\t\tstate.userIntent = parseSectionItems(section.content);\n\t\t\t\tbreak;\n\t\t\tcase \"active files\":\n\t\t\t\tstate.activeFiles = parseSectionItems(section.content);\n\t\t\t\tbreak;\n\t\t\tcase \"decisions\":\n\t\t\t\tstate.decisions = parseSectionItems(section.content);\n\t\t\t\tbreak;\n\t\t\tcase \"constraints\":\n\t\t\t\tstate.constraints = parseSectionItems(section.content);\n\t\t\t\tbreak;\n\t\t\tcase \"errors & corrections\":\n\t\t\t\tstate.errorsAndCorrections = parseSectionItems(section.content);\n\t\t\t\tbreak;\n\t\t\tcase \"next steps\":\n\t\t\t\tstate.nextSteps = parseSectionItems(section.content);\n\t\t\t\tbreak;\n\t\t\tcase \"worklog\":\n\t\t\t\tstate.worklog = parseSectionItems(section.content);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn state;\n}\n\nfunction mergeSessionMemoryState(current: SessionMemoryState, update: SessionMemoryStateUpdate): SessionMemoryState {\n\treturn {\n\t\ttitle: typeof update.title === \"string\" ? update.title : current.title,\n\t\tcurrentState: Array.isArray(update.currentState) ? update.currentState : current.currentState,\n\t\tuserIntent: Array.isArray(update.userIntent) ? update.userIntent : current.userIntent,\n\t\tactiveFiles: Array.isArray(update.activeFiles) ? update.activeFiles : current.activeFiles,\n\t\tdecisions: Array.isArray(update.decisions) ? update.decisions : current.decisions,\n\t\tconstraints: Array.isArray(update.constraints) ? update.constraints : current.constraints,\n\t\terrorsAndCorrections: Array.isArray(update.errorsAndCorrections)\n\t\t\t? update.errorsAndCorrections\n\t\t\t: current.errorsAndCorrections,\n\t\tnextSteps: Array.isArray(update.nextSteps) ? update.nextSteps : current.nextSteps,\n\t\tworklog: Array.isArray(update.worklog) ? update.worklog : current.worklog,\n\t};\n}\n\nasync function writeSessionMemoryDebugFile(channelDir: string, error: unknown, rawText: string): Promise<void> {\n\tconst debugPath = join(channelDir, \"SESSION.invalid-response.txt\");\n\tconst header = [\n\t\t`timestamp: ${new Date().toISOString()}`,\n\t\t`error: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\"\",\n\t\t\"raw response:\",\n\t\t\"\",\n\t].join(\"\\n\");\n\tawait writeFile(debugPath, `${header}${rawText}\\n`, \"utf-8\");\n}\n\nfunction renderSection(heading: string, items: string[]): string {\n\treturn [`# ${heading}`, \"\", ...items.map((item) => `- ${item}`)].join(\"\\n\");\n}\n\nfunction isStandardAgentMessage(message: AgentMessage): message is Message {\n\treturn (\n\t\ttypeof message === \"object\" &&\n\t\tmessage !== null &&\n\t\t\"role\" in message &&\n\t\t(message.role === \"user\" || message.role === \"assistant\" || message.role === \"toolResult\")\n\t);\n}\n\nfunction buildMessagesForSessionMemory(messages: AgentMessage[]): Message[] {\n\treturn messages.filter(isStandardAgentMessage);\n}\n\nexport function renderSessionMemory(state: SessionMemoryState): string {\n\tconst sections: string[] = [];\n\tif (state.title.trim()) {\n\t\tsections.push(\"# Session Title\", \"\", state.title.trim());\n\t}\n\n\tconst orderedSections: Array<[string, string[]]> = [\n\t\t[\"Current State\", state.currentState],\n\t\t[\"User Intent\", state.userIntent],\n\t\t[\"Active Files\", state.activeFiles],\n\t\t[\"Decisions\", state.decisions],\n\t\t[\"Constraints\", state.constraints],\n\t\t[\"Errors & Corrections\", state.errorsAndCorrections],\n\t\t[\"Next Steps\", state.nextSteps],\n\t\t[\"Worklog\", state.worklog],\n\t];\n\n\tfor (const [heading, items] of orderedSections) {\n\t\tif (items.length === 0 && heading === \"Worklog\") {\n\t\t\tcontinue;\n\t\t}\n\t\tsections.push(renderSection(heading, items));\n\t}\n\n\treturn `${sections.join(\"\\n\\n\").trim()}\\n`;\n}\n\nfunction buildSessionPrompt(currentSession: string, currentMemory: string, messages: Message[]): string {\n\tconst transcript = clipText(serializeConversation(messages), SESSION_TRANSCRIPT_MAX_CHARS);\n\treturn `Current SESSION.md:\n${currentSession || \"(empty)\"}\n\nCurrent channel MEMORY.md:\n${clipText(currentMemory, SESSION_MEMORY_MAX_CHARS) || \"(empty)\"}\n\nRecent conversation:\n${transcript || \"(empty)\"}`;\n}\n\nexport async function updateChannelSessionMemory(options: SessionMemoryUpdateOptions): Promise<SessionMemoryState> {\n\tconst currentSession = await readChannelSession(options.channelDir);\n\tconst currentMemory = await readChannelMemory(options.channelDir);\n\tconst messages = buildMessagesForSessionMemory(options.messages);\n\tconst currentState = parseRenderedSessionMemory(currentSession);\n\n\tlet update: SessionMemoryStateUpdate;\n\ttry {\n\t\tconst result = await runSidecarTask({\n\t\t\tname: \"session-memory-update\",\n\t\t\tmodel: options.model,\n\t\t\tresolveApiKey: options.resolveApiKey,\n\t\t\tsystemPrompt: SESSION_MEMORY_SYSTEM_PROMPT,\n\t\t\tprompt: buildSessionPrompt(currentSession, currentMemory, messages),\n\t\t\tparse: parseStateUpdate,\n\t\t\ttimeoutMs: SESSION_MEMORY_TIMEOUT_MS,\n\t\t});\n\t\tupdate = result.output;\n\t} catch (error) {\n\t\tif (error instanceof SidecarParseError) {\n\t\t\tawait writeSessionMemoryDebugFile(options.channelDir, error.cause ?? error, error.rawText);\n\t\t}\n\t\tthrow error;\n\t}\n\n\tconst rendered = renderSessionMemory(mergeSessionMemoryState(currentState, update));\n\tawait rewriteChannelSession(options.channelDir, rendered);\n\treturn parseRenderedSessionMemory(rendered);\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Api, Model } from "@mariozechner/pi-ai";
|
|
2
|
+
export interface SidecarTask<T> {
|
|
3
|
+
name: string;
|
|
4
|
+
model: Model<Api>;
|
|
5
|
+
resolveApiKey: (model: Model<Api>) => Promise<string>;
|
|
6
|
+
systemPrompt: string;
|
|
7
|
+
prompt: string;
|
|
8
|
+
parse: (text: string) => T;
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
export interface SidecarResult<T> {
|
|
13
|
+
output: T;
|
|
14
|
+
rawText: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class SidecarTimeoutError extends Error {
|
|
17
|
+
readonly taskName: string;
|
|
18
|
+
readonly timeoutMs: number;
|
|
19
|
+
constructor(taskName: string, timeoutMs: number);
|
|
20
|
+
}
|
|
21
|
+
export declare class SidecarParseError extends Error {
|
|
22
|
+
readonly taskName: string;
|
|
23
|
+
readonly rawText: string;
|
|
24
|
+
constructor(taskName: string, rawText: string, cause: unknown);
|
|
25
|
+
}
|
|
26
|
+
export declare function runSidecarTask<T>(task: SidecarTask<T>): Promise<SidecarResult<T>>;
|
|
27
|
+
//# sourceMappingURL=sidecar-worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidecar-worker.d.ts","sourceRoot":"","sources":["../src/sidecar-worker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAoB,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAGxE,MAAM,WAAW,WAAW,CAAC,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAM/C;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAO7D;AAaD,wBAAsB,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CA2FvF"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Agent } from "@mariozechner/pi-agent-core";
|
|
2
|
+
import { convertToLlm } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
export class SidecarTimeoutError extends Error {
|
|
4
|
+
constructor(taskName, timeoutMs) {
|
|
5
|
+
super(`Sidecar task "${taskName}" timed out after ${timeoutMs}ms`);
|
|
6
|
+
this.name = "SidecarTimeoutError";
|
|
7
|
+
this.taskName = taskName;
|
|
8
|
+
this.timeoutMs = timeoutMs;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class SidecarParseError extends Error {
|
|
12
|
+
constructor(taskName, rawText, cause) {
|
|
13
|
+
super(`Sidecar task "${taskName}" returned invalid output`);
|
|
14
|
+
this.name = "SidecarParseError";
|
|
15
|
+
this.taskName = taskName;
|
|
16
|
+
this.rawText = rawText;
|
|
17
|
+
this.cause = cause;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function extractAssistantText(message) {
|
|
21
|
+
return message.content
|
|
22
|
+
.filter((part) => part.type === "text")
|
|
23
|
+
.map((part) => part.text)
|
|
24
|
+
.join("\n")
|
|
25
|
+
.trim();
|
|
26
|
+
}
|
|
27
|
+
export async function runSidecarTask(task) {
|
|
28
|
+
const apiKey = await task.resolveApiKey(task.model);
|
|
29
|
+
const worker = new Agent({
|
|
30
|
+
initialState: {
|
|
31
|
+
systemPrompt: task.systemPrompt,
|
|
32
|
+
model: task.model,
|
|
33
|
+
thinkingLevel: "off",
|
|
34
|
+
tools: [],
|
|
35
|
+
},
|
|
36
|
+
convertToLlm,
|
|
37
|
+
getApiKey: async () => apiKey,
|
|
38
|
+
});
|
|
39
|
+
const abortWorker = () => {
|
|
40
|
+
try {
|
|
41
|
+
worker.abort();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
/* ignore */
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
let removeAbortListener = () => { };
|
|
48
|
+
let timeoutHandle = null;
|
|
49
|
+
const runPromise = (async () => {
|
|
50
|
+
await worker.prompt(task.prompt);
|
|
51
|
+
await worker.waitForIdle();
|
|
52
|
+
const lastMessage = worker.state.messages[worker.state.messages.length - 1];
|
|
53
|
+
if (!lastMessage || lastMessage.role !== "assistant") {
|
|
54
|
+
throw new Error(`Sidecar task "${task.name}" returned no assistant message`);
|
|
55
|
+
}
|
|
56
|
+
if (lastMessage.stopReason === "error" || lastMessage.stopReason === "aborted") {
|
|
57
|
+
throw new Error(lastMessage.errorMessage || `Sidecar task "${task.name}" failed`);
|
|
58
|
+
}
|
|
59
|
+
const rawText = extractAssistantText(lastMessage);
|
|
60
|
+
try {
|
|
61
|
+
return {
|
|
62
|
+
output: task.parse(rawText),
|
|
63
|
+
rawText,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new SidecarParseError(task.name, rawText, error);
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
70
|
+
void runPromise.catch(() => { });
|
|
71
|
+
const blockers = [];
|
|
72
|
+
if (task.timeoutMs && task.timeoutMs > 0) {
|
|
73
|
+
blockers.push(new Promise((_, reject) => {
|
|
74
|
+
timeoutHandle = setTimeout(() => {
|
|
75
|
+
abortWorker();
|
|
76
|
+
reject(new SidecarTimeoutError(task.name, task.timeoutMs));
|
|
77
|
+
}, task.timeoutMs);
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
if (task.signal) {
|
|
81
|
+
const signal = task.signal;
|
|
82
|
+
blockers.push(new Promise((_, reject) => {
|
|
83
|
+
const abort = () => {
|
|
84
|
+
abortWorker();
|
|
85
|
+
reject(signal.reason instanceof Error ? signal.reason : new Error(`Sidecar task "${task.name}" aborted`));
|
|
86
|
+
};
|
|
87
|
+
if (signal.aborted) {
|
|
88
|
+
abort();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
92
|
+
removeAbortListener = () => signal.removeEventListener("abort", abort);
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
return blockers.length > 0 ? await Promise.race([runPromise, ...blockers]) : await runPromise;
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
if (timeoutHandle) {
|
|
100
|
+
clearTimeout(timeoutHandle);
|
|
101
|
+
}
|
|
102
|
+
removeAbortListener();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=sidecar-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidecar-worker.js","sourceRoot":"","sources":["../src/sidecar-worker.ts"],"names":[],"mappings":"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","sourcesContent":["import { Agent } from \"@mariozechner/pi-agent-core\";\nimport type { Api, AssistantMessage, Model } from \"@mariozechner/pi-ai\";\nimport { convertToLlm } from \"@mariozechner/pi-coding-agent\";\n\nexport interface SidecarTask<T> {\n\tname: string;\n\tmodel: Model<Api>;\n\tresolveApiKey: (model: Model<Api>) => Promise<string>;\n\tsystemPrompt: string;\n\tprompt: string;\n\tparse: (text: string) => T;\n\ttimeoutMs?: number;\n\tsignal?: AbortSignal;\n}\n\nexport interface SidecarResult<T> {\n\toutput: T;\n\trawText: string;\n}\n\nexport class SidecarTimeoutError extends Error {\n\treadonly taskName: string;\n\treadonly timeoutMs: number;\n\n\tconstructor(taskName: string, timeoutMs: number) {\n\t\tsuper(`Sidecar task \"${taskName}\" timed out after ${timeoutMs}ms`);\n\t\tthis.name = \"SidecarTimeoutError\";\n\t\tthis.taskName = taskName;\n\t\tthis.timeoutMs = timeoutMs;\n\t}\n}\n\nexport class SidecarParseError extends Error {\n\treadonly taskName: string;\n\treadonly rawText: string;\n\n\tconstructor(taskName: string, rawText: string, cause: unknown) {\n\t\tsuper(`Sidecar task \"${taskName}\" returned invalid output`);\n\t\tthis.name = \"SidecarParseError\";\n\t\tthis.taskName = taskName;\n\t\tthis.rawText = rawText;\n\t\tthis.cause = cause;\n\t}\n}\n\nfunction extractAssistantText(message: AssistantMessage): string {\n\treturn message.content\n\t\t.filter(\n\t\t\t(part): part is Extract<AssistantMessage[\"content\"][number], { type: \"text\"; text: string }> =>\n\t\t\t\tpart.type === \"text\",\n\t\t)\n\t\t.map((part) => part.text)\n\t\t.join(\"\\n\")\n\t\t.trim();\n}\n\nexport async function runSidecarTask<T>(task: SidecarTask<T>): Promise<SidecarResult<T>> {\n\tconst apiKey = await task.resolveApiKey(task.model);\n\tconst worker = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: task.systemPrompt,\n\t\t\tmodel: task.model,\n\t\t\tthinkingLevel: \"off\",\n\t\t\ttools: [],\n\t\t},\n\t\tconvertToLlm,\n\t\tgetApiKey: async () => apiKey,\n\t});\n\n\tconst abortWorker = () => {\n\t\ttry {\n\t\t\tworker.abort();\n\t\t} catch {\n\t\t\t/* ignore */\n\t\t}\n\t};\n\n\tlet removeAbortListener = () => {};\n\tlet timeoutHandle: NodeJS.Timeout | null = null;\n\n\tconst runPromise = (async () => {\n\t\tawait worker.prompt(task.prompt);\n\t\tawait worker.waitForIdle();\n\n\t\tconst lastMessage = worker.state.messages[worker.state.messages.length - 1];\n\t\tif (!lastMessage || lastMessage.role !== \"assistant\") {\n\t\t\tthrow new Error(`Sidecar task \"${task.name}\" returned no assistant message`);\n\t\t}\n\n\t\tif (lastMessage.stopReason === \"error\" || lastMessage.stopReason === \"aborted\") {\n\t\t\tthrow new Error(lastMessage.errorMessage || `Sidecar task \"${task.name}\" failed`);\n\t\t}\n\n\t\tconst rawText = extractAssistantText(lastMessage);\n\t\ttry {\n\t\t\treturn {\n\t\t\t\toutput: task.parse(rawText),\n\t\t\t\trawText,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tthrow new SidecarParseError(task.name, rawText, error);\n\t\t}\n\t})();\n\tvoid runPromise.catch(() => {});\n\n\tconst blockers: Array<Promise<never>> = [];\n\tif (task.timeoutMs && task.timeoutMs > 0) {\n\t\tblockers.push(\n\t\t\tnew Promise<never>((_, reject) => {\n\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\tabortWorker();\n\t\t\t\t\treject(new SidecarTimeoutError(task.name, task.timeoutMs!));\n\t\t\t\t}, task.timeoutMs);\n\t\t\t}),\n\t\t);\n\t}\n\n\tif (task.signal) {\n\t\tconst signal = task.signal;\n\t\tblockers.push(\n\t\t\tnew Promise<never>((_, reject) => {\n\t\t\t\tconst abort = () => {\n\t\t\t\t\tabortWorker();\n\t\t\t\t\treject(\n\t\t\t\t\t\tsignal.reason instanceof Error ? signal.reason : new Error(`Sidecar task \"${task.name}\" aborted`),\n\t\t\t\t\t);\n\t\t\t\t};\n\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tabort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t\t\t\tremoveAbortListener = () => signal.removeEventListener(\"abort\", abort);\n\t\t\t}),\n\t\t);\n\t}\n\n\ttry {\n\t\treturn blockers.length > 0 ? await Promise.race([runPromise, ...blockers]) : await runPromise;\n\t} finally {\n\t\tif (timeoutHandle) {\n\t\t\tclearTimeout(timeoutHandle);\n\t\t}\n\t\tremoveAbortListener();\n\t}\n}\n"]}
|
package/dist/sub-agents.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { Api, Model } from "@mariozechner/pi-ai";
|
|
2
2
|
declare const ALLOWED_SUB_AGENT_TOOLS: readonly ["read", "bash", "edit", "write"];
|
|
3
|
+
declare const ALLOWED_CONTEXT_MODES: readonly ["isolated", "contextual"];
|
|
4
|
+
declare const ALLOWED_MEMORY_MODES: readonly ["none", "session", "relevant"];
|
|
3
5
|
export type SubAgentToolName = (typeof ALLOWED_SUB_AGENT_TOOLS)[number];
|
|
6
|
+
export type SubAgentContextMode = (typeof ALLOWED_CONTEXT_MODES)[number];
|
|
7
|
+
export type SubAgentMemoryMode = (typeof ALLOWED_MEMORY_MODES)[number];
|
|
4
8
|
export interface SubAgentConfig {
|
|
5
9
|
name: string;
|
|
6
10
|
description: string;
|
|
@@ -12,6 +16,9 @@ export interface SubAgentConfig {
|
|
|
12
16
|
maxToolCalls: number;
|
|
13
17
|
maxWallTimeSec: number;
|
|
14
18
|
bashTimeoutSec: number;
|
|
19
|
+
contextMode: SubAgentContextMode;
|
|
20
|
+
memory: SubAgentMemoryMode;
|
|
21
|
+
paths: string[];
|
|
15
22
|
filePath?: string;
|
|
16
23
|
source: "predefined" | "inline";
|
|
17
24
|
}
|
|
@@ -34,6 +41,9 @@ export interface SubAgentInvocationOverrides {
|
|
|
34
41
|
maxToolCalls?: number;
|
|
35
42
|
maxWallTimeSec?: number;
|
|
36
43
|
bashTimeoutSec?: number;
|
|
44
|
+
contextMode?: string;
|
|
45
|
+
memory?: string;
|
|
46
|
+
paths?: string[];
|
|
37
47
|
}
|
|
38
48
|
export declare function validateSubAgentTask(task: string): string | undefined;
|
|
39
49
|
export declare function getSubAgentsDir(workspaceDir: string): string;
|