@oyasmi/pipiclaw 0.5.4 → 0.5.5

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 CHANGED
@@ -140,6 +140,7 @@ pipiclaw
140
140
  ├── SOUL.md
141
141
  ├── AGENTS.md
142
142
  ├── MEMORY.md
143
+ ├── ENVIRONMENT.md
143
144
  ├── events/
144
145
  ├── skills/
145
146
  └── sub-agents/
@@ -385,6 +386,7 @@ Pipiclaw 的核心不是一个临时机器人实例,而是一组长期存在
385
386
  ├── SOUL.md
386
387
  ├── AGENTS.md
387
388
  ├── MEMORY.md
389
+ ├── ENVIRONMENT.md
388
390
  ├── events/
389
391
  ├── skills/
390
392
  ├── sub-agents/
@@ -395,8 +397,7 @@ Pipiclaw 的核心不是一个临时机器人实例,而是一组长期存在
395
397
  │ ├── .channel-meta.json
396
398
  │ ├── context.jsonl
397
399
  │ ├── log.jsonl
398
- ├── subagent-runs.jsonl
399
- │ └── skills/
400
+ └── subagent-runs.jsonl
400
401
  └── group_{conversationId}/
401
402
  └── ...
402
403
  ```
@@ -31,6 +31,7 @@ ${workspacePath}/
31
31
  ├── SOUL.md # Your identity/personality (read-only)
32
32
  ├── AGENTS.md # Custom behavior instructions (read-only)
33
33
  ├── MEMORY.md # Stable workspace memory (admin-managed, read on demand)
34
+ ├── ENVIRONMENT.md # Environment facts and notable machine-level changes (read on demand)
34
35
  ├── sub-agents/ # Predefined sub-agent definitions
35
36
  ├── skills/ # Global CLI tools you create
36
37
  ├── events/ # Scheduled events
@@ -39,9 +40,7 @@ ${workspacePath}/
39
40
  ├── MEMORY.md # Channel durable memory (read on demand, runtime-managed)
40
41
  ├── HISTORY.md # Channel summarized history (read on demand, runtime-managed)
41
42
  ├── log.jsonl # Raw message archive (cold storage)
42
- ├── context.jsonl # Raw session archive (cold storage)
43
- ├── scratch/ # Your working directory
44
- └── skills/ # Channel-specific tools`);
43
+ └── context.jsonl # Raw session archive (cold storage)`);
45
44
  sections.push(`## Events
46
45
  You can schedule events that wake you up at specific times or when external things happen. Events are JSON files in \`${workspacePath}/events/\`.
47
46
 
@@ -80,6 +79,8 @@ Memory files are not preloaded into session context. Read them explicitly when m
80
79
  ### Files
81
80
  - Workspace memory: ${workspacePath}/MEMORY.md
82
81
  Stable shared background memory. Admin-managed. Read on demand.
82
+ - Workspace environment: ${workspacePath}/ENVIRONMENT.md
83
+ Durable environment facts and notable machine-level changes. Read on demand when environment state or prior machine changes matter.
83
84
  - Channel session memory: ${channelPath}/SESSION.md
84
85
  Current working state for this channel. Runtime-managed. Read on demand. Prefer this when current task state matters.
85
86
  - Channel memory: ${channelPath}/MEMORY.md
@@ -92,20 +93,21 @@ Memory files are not preloaded into session context. Read them explicitly when m
92
93
  - SESSION.md is the primary runtime-managed working-state artifact for current active work.
93
94
  - The runtime automatically consolidates channel MEMORY.md and HISTORY.md before compaction or session trimming.
94
95
  - Workspace MEMORY.md is not updated by normal runtime consolidation.
96
+ - ENVIRONMENT.md is not normal conversational memory. Read it only when environment history or machine state matters.
95
97
 
96
98
  ### Cold Storage
97
99
  - ${channelPath}/log.jsonl is a raw archive. It is not normal memory and is not proactively loaded.
98
100
  - ${channelPath}/context.jsonl is a raw session archive. It is not normal memory and is not proactively loaded.
99
101
 
100
102
  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.`);
101
- sections.push(`## System Configuration Log
102
- Maintain ${workspacePath}/SYSTEM.md to log all environment modifications:
103
- - Installed packages (apk add, npm install, pip install)
104
- - Environment variables set
105
- - Config files modified
106
- - Skill dependencies installed
107
-
108
- Update this file whenever you modify the environment.`);
103
+ sections.push(`## Environment Log
104
+ Maintain ${workspacePath}/ENVIRONMENT.md to record durable environment changes when they matter:
105
+ - Installed packages or tools that future work depends on
106
+ - Important environment variables or credential sources
107
+ - Config files modified outside normal project code
108
+ - Runtime prerequisites that affect future sessions
109
+
110
+ Keep it factual and concise. Do not use it for task progress or conversation summaries.`);
109
111
  sections.push(`## Tools
110
112
  - read: Read files
111
113
  - edit: Surgical file edits
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Workspace resource loaders for pipiclaw:
3
- * SOUL.md, AGENTS.md, and workspace/channel skills.
3
+ * SOUL.md, AGENTS.md, and workspace-level skills.
4
4
  */
5
5
  import { type Skill } from "@mariozechner/pi-coding-agent";
6
6
  /**
@@ -14,7 +14,6 @@ export declare function getSoul(workspaceDir: string): string;
14
14
  */
15
15
  export declare function getAgentConfig(channelDir: string): string;
16
16
  /**
17
- * Load skills from both workspace-level and channel-level skill directories.
18
- * Channel-level skills override global skills with the same name.
17
+ * Load skills from the workspace-level skill directory only.
19
18
  */
20
19
  export declare function loadPipiclawSkills(channelDir: string, workspacePath: string): Skill[];
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Workspace resource loaders for pipiclaw:
3
- * SOUL.md, AGENTS.md, and workspace/channel skills.
3
+ * SOUL.md, AGENTS.md, and workspace-level skills.
4
4
  */
5
5
  import { loadSkillsFromDir } from "@mariozechner/pi-coding-agent";
6
6
  import { existsSync, readFileSync } from "fs";
@@ -44,11 +44,9 @@ export function getAgentConfig(channelDir) {
44
44
  return "";
45
45
  }
46
46
  /**
47
- * Load skills from both workspace-level and channel-level skill directories.
48
- * Channel-level skills override global skills with the same name.
47
+ * Load skills from the workspace-level skill directory only.
49
48
  */
50
49
  export function loadPipiclawSkills(channelDir, workspacePath) {
51
- const skillMap = new Map();
52
50
  const hostWorkspacePath = join(channelDir, "..");
53
51
  const translatePath = (hostPath) => {
54
52
  if (hostPath.startsWith(hostWorkspacePath)) {
@@ -56,19 +54,11 @@ export function loadPipiclawSkills(channelDir, workspacePath) {
56
54
  }
57
55
  return hostPath;
58
56
  };
59
- // Load workspace-level skills (global)
60
57
  const workspaceSkillsDir = join(hostWorkspacePath, "skills");
61
- for (const skill of loadSkillsFromDir({ dir: workspaceSkillsDir, source: "workspace" }).skills) {
58
+ const skills = loadSkillsFromDir({ dir: workspaceSkillsDir, source: "workspace" }).skills;
59
+ for (const skill of skills) {
62
60
  skill.filePath = translatePath(skill.filePath);
63
61
  skill.baseDir = translatePath(skill.baseDir);
64
- skillMap.set(skill.name, skill);
65
62
  }
66
- // Load channel-specific skills
67
- const channelSkillsDir = join(channelDir, "skills");
68
- for (const skill of loadSkillsFromDir({ dir: channelSkillsDir, source: "channel" }).skills) {
69
- skill.filePath = translatePath(skill.filePath);
70
- skill.baseDir = translatePath(skill.baseDir);
71
- skillMap.set(skill.name, skill);
72
- }
73
- return Array.from(skillMap.values());
63
+ return skills;
74
64
  }
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export { runSidecarTask, type SidecarResult, type SidecarTask, } from "./memory/
13
13
  export { getApiKeyForModel } from "./models/api-keys.js";
14
14
  export { findExactModelReferenceMatch, formatModelList, formatModelReference, resolveInitialModel, } from "./models/utils.js";
15
15
  export { APP_HOME_DIR, APP_NAME, AUTH_CONFIG_PATH, CHANNEL_CONFIG_PATH, MODELS_CONFIG_PATH, SETTINGS_CONFIG_PATH, SUB_AGENTS_DIR, SUB_AGENTS_DIR_NAME, WORKSPACE_DIR, } from "./paths.js";
16
+ export { ensureChannelDir, getChannelDir, getChannelDirName, } from "./runtime/channel-paths.js";
16
17
  export { createDingTalkContext } from "./runtime/delivery.js";
17
18
  export { type BusyMessageMode, DingTalkBot, type DingTalkConfig, type DingTalkContext, type DingTalkEvent, type DingTalkHandler, } from "./runtime/dingtalk.js";
18
19
  export { createEventsWatcher, EventsWatcher, type ImmediateEvent, type OneShotEvent, type PeriodicEvent, type ScheduledEvent, } from "./runtime/events.js";
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ export { runSidecarTask, } from "./memory/sidecar-worker.js";
13
13
  export { getApiKeyForModel } from "./models/api-keys.js";
14
14
  export { findExactModelReferenceMatch, formatModelList, formatModelReference, resolveInitialModel, } from "./models/utils.js";
15
15
  export { APP_HOME_DIR, APP_NAME, AUTH_CONFIG_PATH, CHANNEL_CONFIG_PATH, MODELS_CONFIG_PATH, SETTINGS_CONFIG_PATH, SUB_AGENTS_DIR, SUB_AGENTS_DIR_NAME, WORKSPACE_DIR, } from "./paths.js";
16
+ export { ensureChannelDir, getChannelDir, getChannelDirName, } from "./runtime/channel-paths.js";
16
17
  export { createDingTalkContext } from "./runtime/delivery.js";
17
18
  export { DingTalkBot, } from "./runtime/dingtalk.js";
18
19
  export { createEventsWatcher, EventsWatcher, } from "./runtime/events.js";
@@ -6,6 +6,7 @@ import * as log from "../log.js";
6
6
  import { ensureChannelMemoryFilesSync } from "../memory/files.js";
7
7
  import { APP_HOME_DIR, APP_NAME, AUTH_CONFIG_PATH, CHANNEL_CONFIG_PATH, MODELS_CONFIG_PATH, SETTINGS_CONFIG_PATH, WORKSPACE_DIR, } from "../paths.js";
8
8
  import { parseSandboxArg, validateSandbox } from "../sandbox.js";
9
+ import { ensureChannelDir } from "./channel-paths.js";
9
10
  import { createDingTalkContext } from "./delivery.js";
10
11
  import { DingTalkBot, } from "./dingtalk.js";
11
12
  import { createEventsWatcher } from "./events.js";
@@ -68,6 +69,26 @@ This file stores stable workspace-level memory.
68
69
 
69
70
  <!-- Put long-lived project facts here. -->
70
71
  `;
72
+ const DEFAULT_ENVIRONMENT = `# Environment
73
+
74
+ This file records durable environment facts and notable machine-level changes.
75
+
76
+ - Record installed tools, runtime prerequisites, and important config changes here.
77
+ - Keep entries concise and factual.
78
+ - Do not use this file for task progress, conversation summaries, or project-specific decisions.
79
+
80
+ ## Environment Facts
81
+
82
+ <!-- Put stable machine or runtime facts here. -->
83
+
84
+ ## Installed Tools
85
+
86
+ <!-- Record durable tools or dependencies that were installed for this workspace. -->
87
+
88
+ ## Config Changes
89
+
90
+ <!-- Record important config or environment changes that affect future work. -->
91
+ `;
71
92
  const CHANNEL_CONFIG_TEMPLATE = {
72
93
  clientId: "your-dingtalk-client-id",
73
94
  clientSecret: "your-dingtalk-client-secret",
@@ -139,6 +160,7 @@ export function bootstrapAppHome(paths = DEFAULT_BOOTSTRAP_PATHS) {
139
160
  writeTextFileIfMissing(join(paths.workspaceDir, "SOUL.md"), DEFAULT_SOUL, "workspace/SOUL.md", created);
140
161
  writeTextFileIfMissing(join(paths.workspaceDir, "AGENTS.md"), DEFAULT_AGENT, "workspace/AGENTS.md", created);
141
162
  writeTextFileIfMissing(join(paths.workspaceDir, "MEMORY.md"), DEFAULT_MEMORY, "workspace/MEMORY.md", created);
163
+ writeTextFileIfMissing(join(paths.workspaceDir, "ENVIRONMENT.md"), DEFAULT_ENVIRONMENT, "workspace/ENVIRONMENT.md", created);
142
164
  const channelTemplateCreated = writeJsonFileIfMissing(paths.channelConfigPath, CHANNEL_CONFIG_TEMPLATE, "channel.json", created);
143
165
  writeJsonFileIfMissing(paths.authConfigPath, {}, "auth.json", created);
144
166
  writeJsonFileIfMissing(paths.modelsConfigPath, MODELS_CONFIG_TEMPLATE, "models.json", created);
@@ -273,7 +295,7 @@ export async function bootstrap(argv, options = {}) {
273
295
  const getState = (channelId) => {
274
296
  let state = channelStates.get(channelId);
275
297
  if (!state) {
276
- const channelDir = join(paths.workspaceDir, channelId);
298
+ const channelDir = ensureChannelDir(paths.workspaceDir, channelId);
277
299
  ensureChannelMemoryFilesSync(channelDir);
278
300
  state = {
279
301
  running: false,
@@ -0,0 +1,3 @@
1
+ export declare function getChannelDirName(channelId: string): string;
2
+ export declare function getChannelDir(baseDir: string, channelId: string): string;
3
+ export declare function ensureChannelDir(baseDir: string, channelId: string): string;
@@ -0,0 +1,13 @@
1
+ import { mkdirSync } from "fs";
2
+ import { join } from "path";
3
+ export function getChannelDirName(channelId) {
4
+ return channelId.replaceAll("/", "__");
5
+ }
6
+ export function getChannelDir(baseDir, channelId) {
7
+ return join(baseDir, getChannelDirName(channelId));
8
+ }
9
+ export function ensureChannelDir(baseDir, channelId) {
10
+ const channelDir = getChannelDir(baseDir, channelId);
11
+ mkdirSync(channelDir, { recursive: true });
12
+ return channelDir;
13
+ }
@@ -14,6 +14,7 @@ import { dirname, join } from "path";
14
14
  import { parseBuiltInCommand, renderBuiltInHelp } from "../agent/commands.js";
15
15
  import * as log from "../log.js";
16
16
  import { isRecord } from "../shared/type-guards.js";
17
+ import { getChannelDir } from "./channel-paths.js";
17
18
  class ChannelQueue {
18
19
  constructor() {
19
20
  this.queue = [];
@@ -761,6 +762,6 @@ export class DingTalkBot {
761
762
  getConversationMetaPath(channelId) {
762
763
  if (!this.config.stateDir)
763
764
  return null;
764
- return join(this.config.stateDir, channelId, ".channel-meta.json");
765
+ return join(getChannelDir(this.config.stateDir, channelId), ".channel-meta.json");
765
766
  }
766
767
  }
@@ -1,6 +1,7 @@
1
1
  import { closeSync, existsSync, mkdirSync, openSync, readSync, renameSync, statSync } from "fs";
2
2
  import { appendFile, writeFile } from "fs/promises";
3
3
  import { dirname, join } from "path";
4
+ import { ensureChannelDir, getChannelDir } from "./channel-paths.js";
4
5
  const MAX_LOG_SIZE_BYTES = 1_000_000;
5
6
  const DEDUPE_TTL_MS = 60_000;
6
7
  const DEDUPE_CLEANUP_INTERVAL_MS = 30_000;
@@ -19,11 +20,7 @@ export class ChannelStore {
19
20
  * Get or create the directory for a channel/DM
20
21
  */
21
22
  getChannelDir(channelId) {
22
- const dir = join(this.workingDir, channelId);
23
- if (!existsSync(dir)) {
24
- mkdirSync(dir, { recursive: true });
25
- }
26
- return dir;
23
+ return ensureChannelDir(this.workingDir, channelId);
27
24
  }
28
25
  /**
29
26
  * Log a message to the channel's log.jsonl raw archive.
@@ -102,7 +99,7 @@ export class ChannelStore {
102
99
  * Returns null if no log exists
103
100
  */
104
101
  getLastTimestamp(channelId) {
105
- const logPath = join(this.workingDir, channelId, "log.jsonl");
102
+ const logPath = join(getChannelDir(this.workingDir, channelId), "log.jsonl");
106
103
  if (!existsSync(logPath)) {
107
104
  return null;
108
105
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oyasmi/pipiclaw",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "An AI assistant runtime for coding and team workflows, with DingTalk AI Cards, sub-agents, memory, and scheduled events.",
5
5
  "type": "module",
6
6
  "bin": {