@zetaloop/chappie 0.4.0 → 0.4.1

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
@@ -53,10 +53,10 @@ The default port is `24274`. Set `listen` to a port number or append `:port` to
53
53
 
54
54
  Set `ask` to `false` to disable webpage questions.
55
55
 
56
- Optional session synchronization ends duplicate executions so one execution continues:
56
+ Enable synchronization to resolve conflicting activity:
57
57
 
58
58
  ```json
59
59
  { "sync": true }
60
60
  ```
61
61
 
62
- With synchronization enabled, initialization returns a fresh code. `sync` locks a Pi session and its bound conversations while executions verify their codes. Rejected executions announce their exit and end their responses; the verified execution confirms those exits through `chat` and `history`, then releases the lock. Codes persist in broker state; locks last for the broker process. See [synchronization](docs/tools.md#synchronization) for the tool sequence.
62
+ Initialization returns a short name and a private code. `sync` pauses conflicting work for discussion through `chat` and `history`. The verified coordinator decides who continues, their tasks, and who exits, including retaining only one execution. See [synchronization](docs/tools.md#synchronization).
package/docs/tools.md CHANGED
@@ -4,7 +4,7 @@
4
4
  |---|---|
5
5
  | `init` | Select this ChatGPT conversation's default Pi session and read its environment. |
6
6
  | `history` | Read the current Pi branch with timestamps and entry IDs. |
7
- | `sync` | End duplicate executions so one execution continues. |
7
+ | `sync` | Resolve conflicting activity under one coordinator. |
8
8
  | `sessions` | List connected Pi sessions and the current default. |
9
9
  | `tools` | Read full definitions of active Pi tools for `call`. |
10
10
  | `chat` | Send an assistant message to Pi. |
@@ -21,6 +21,8 @@
21
21
 
22
22
  Call `init` at the start of local work. Without `sessionId`, it reuses the conversation's saved default or selects an online Pi session with no saved ChatGPT binding. Pass a Pi session ID to resume a specific task, including from another ChatGPT conversation or branch. Read recent `history` to recover progress before continuing the current task.
23
23
 
24
+ Initialization returns the request suffix as `initialization.name` when available. Each execution retains its own name for coordination through `chat`.
25
+
24
26
  `sessions` lists connected sessions with their ID, device, working directory, name, execution status, and binding count. The first execution tool call establishes the default using its `sessionId` or an online session with no saved bindings. During synchronization, `chat` and `history` use the requested session solely for communication. Once a default exists, another tool's `sessionId` selects only that operation's target; `init({ sessionId })` changes the default.
25
27
 
26
28
  Several ChatGPT conversations can use the same Pi session. One conversation can also operate on several Pi sessions explicitly. Requests already assigned to a session continue there even if the conversation later changes its default. Synchronization can cancel ordinary requests for the locked session or its bound conversations.
@@ -37,23 +39,21 @@ Remote Pi sessions appear in the same list when they connect to a broker exposed
37
39
 
38
40
  Omit `before` for the latest entries. Use `after` to read forward from an entry. Both fields can delimit a range, with the named entries outside the returned range. The default limit is 20 readable entries. Results follow branch order and contain each entry's original ID and timestamp. `hasMore` indicates additional entries in the requested direction.
39
41
 
40
- Messages, tool calls and results, summaries, images, file links, and Chappie activity records use their saved contents, including Pi's existing truncation notices and full-output paths. Assistant messages carry their originating `chatId` and optional full `requestId` in `message.chappie`. Tool results inherit the source of their `toolCallId`, including when the call falls outside the requested page. Activity records carry the same source fields. Request-specific notices display a compact label such as `ChatGPT Zxbs(fd44) joined`; the workflow suffix is for log correlation and can be shared by duplicate executions. Reading leaves a short notice in Pi; the returned history remains separate from new input and pending result delivery.
42
+ Messages, tool calls and results, summaries, images, file links, and Chappie activity records use their saved contents, including Pi's existing truncation notices and full-output paths. Assistant messages carry their originating `chatId` and optional full `requestId` in `message.chappie`. Tool results inherit the source of their `toolCallId`, including when the call falls outside the requested page. Activity records carry the same source fields. Request-specific notices display a compact label such as `ChatGPT Zxbs(fd44) joined`; the workflow suffix is for log correlation and can be shared by parallel executions. When participants report different goals, workflow timing helps identify possible work resumed from an earlier ChatGPT message. Reading leaves a short notice in Pi; the returned history remains separate from new input and pending result delivery.
41
43
 
42
44
  Pi user input, webpage answers, connection status, and deferred delivery target sessions or conversations. A deferred result's `requestId` identifies the original operation; the result can reach another execution in that conversation.
43
45
 
44
46
  ## Synchronization
45
47
 
46
- Set `sync` to `true` in the broker's `chappie.json` to enable the `sync` tool. Each explicit initialization and first execution that establishes a default returns `initialization.code`. Retain the code in the ChatGPT context alongside its Pi session ID. Ordinary calls use the existing binding; a saved binding without a code receives one on its next execution.
48
+ Enable `sync` in the broker's `chappie.json`. Initialization returns a private `initialization.code` for its Pi session; a saved binding without a code receives one on its next execution.
47
49
 
48
- When activity conflicts, start synchronization for the session:
50
+ When activity conflicts, start synchronization:
49
51
 
50
52
  ```json
51
53
  { "action": "start", "sessionId": "<session-id>" }
52
54
  ```
53
55
 
54
- This locks ordinary tools and initialization for that Pi session and every conversation currently bound to it. Existing ordinary requests in that scope are cancelled through Pi's cancellation path. Bound conversations also pause their operations on other sessions. Other conversations continue using unrelated sessions. Automatic session selection considers unlocked sessions.
55
-
56
- `chat`, `history`, and `sessions` remain available to establish which executions must exit. A rejected execution uses `chat` for one final message with its task, entry time, and explicit exit statement, then immediately ends its ChatGPT response and all tool use. Unlocking leaves that execution finished; it must not wait, poll, reinitialize, or resume the task. The verified execution remains responsible for completing the current task. Communication tools keep existing bindings during synchronization. Pending input, webpage answers, and deferred results for the affected work remain available for normal delivery after release. Webpage components can still submit answers and the host can read exported resources.
56
+ This locks ordinary tools and initialization for the Pi session and its bound conversations, cancelling their active ordinary requests. Bound conversations also pause work on other sessions; unrelated conversations continue. `chat`, `history`, and `sessions` remain available with existing bindings.
57
57
 
58
58
  Verify using the code from the most recent initialization:
59
59
 
@@ -61,15 +61,17 @@ Verify using the code from the most recent initialization:
61
61
  { "action": "verify", "code": "<initialization-code>" }
62
62
  ```
63
63
 
64
- The first successful verification replaces the code and returns it to that call. Verification leaves the session locked. A missing, incorrect, or older code identifies an accidental duplicate that must exit as described above. This applies to the execution, even when other executions share its conversation ID. Repeating `start` preserves the current synchronization; repeating `verify` with the verified code reads its status.
64
+ The first successful verification returns a replacement code to the coordinator. Everyone can discuss goals and progress through `chat` and `history`, including executions with missing or rejected codes. The coordinator decides who continues, their tasks, and who exits, and may retain only one execution.
65
65
 
66
- The holder of the verified code uses `chat` and `history` to identify every observed conflicting execution, require an explicit final exit message, and confirm it has ended its response. A promise to pause ordinary tools, idle Pi status, or a quiet history page alone is insufficient. After those final exits and resolution of conflicting activity, the surviving execution explicitly releases the session:
66
+ Participants acknowledge the decision. Those directed to exit leave a chat handoff and end their responses. Only the coordinator can release, after these decisions and exits are confirmed:
67
67
 
68
68
  ```json
69
69
  { "action": "release", "code": "<verified-code>" }
70
70
  ```
71
71
 
72
- Current codes are saved per Pi session in `chappie.state.json`. Activity records contain coordination details; codes stay in initialization and synchronization exchanges. Lock and verification state live in the broker process, so restarting the broker restores ordinary operation. Pi-client and tunnel reconnections retain the running broker's locks. `sessions` and `history` report the current synchronization state.
72
+ Remaining executions resume their assigned work. Pending input, webpage answers, and deferred results resume normal delivery. Webpage submissions and exported resource reads remain available throughout synchronization.
73
+
74
+ Codes persist per Pi session in `chappie.state.json`; locks last for the broker process and survive Pi-client or tunnel reconnections. Repeated `start` preserves the lock; `verify` with the current code, `sessions`, and `history` report its status.
73
75
 
74
76
  ## Pi tools
75
77
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zetaloop/chappie",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Connect ChatGPT to Pi",
5
5
  "devDependencies": {
6
6
  "@earendil-works/pi-ai": "^0.85.1",
package/src/broker.ts CHANGED
@@ -25,8 +25,8 @@ import { type ResourceData, resourceSessionId } from "./resources.ts";
25
25
  import { State } from "./state.ts";
26
26
  import type { ToolInput } from "./tools.ts";
27
27
 
28
- const exitInstructions =
29
- "Send one final chat message stating your task, entry time, and that this execution is ending; keep all codes private. Then end this ChatGPT response immediately with no further tool calls. Do not wait for unlock, poll history, reinitialize, or resume this task after release.";
28
+ const coordinationInstructions =
29
+ "Report your goal and progress through chat using your initialization name. The coordinator decides who continues, their tasks, and who exits. Follow the decision through chat and history; if asked to exit, leave a handoff and end this response.";
30
30
 
31
31
  interface RegisteredSession {
32
32
  description: SessionDescription;
@@ -66,6 +66,7 @@ interface ChangeWaiter {
66
66
  }
67
67
 
68
68
  export interface Initialization {
69
+ name?: string;
69
70
  code?: string;
70
71
  sessionId: string;
71
72
  instructions: string;
@@ -304,7 +305,7 @@ export class Broker {
304
305
  { ...activity, event: "sync_rejected" },
305
306
  );
306
307
  throw new Error(
307
- `The sync code is missing or outdated. This execution is a stale, accidental duplicate. ${exitInstructions}`,
308
+ `Invalid sync code. Ordinary tools remain locked. ${coordinationInstructions}`,
308
309
  );
309
310
  }
310
311
  if (action === "verify") {
@@ -324,7 +325,7 @@ export class Broker {
324
325
  ...this.syncState(target),
325
326
  ...(renewed ? { code: renewed } : {}),
326
327
  instructions:
327
- "This is the surviving execution. Keep the verified code private in this ChatGPT context. Use chat and history to identify every observed conflicting execution and require its final exit. Confirm they have ended their responses, rather than only paused ordinary tools. Verification, idle Pi status, or a quiet history page alone does not establish their exit. Release with this code only after explicit final exit statements and resolution of conflicting activity; ordinary tools remain locked until then.",
328
+ "You are the coordinator. Decide who continues, their tasks, and who exits; you may retain only one execution. Keep this code private. Release after your decisions are acknowledged and requested exits are complete.",
328
329
  };
329
330
  }
330
331
  if (!this.#locks.get(target))
@@ -342,7 +343,7 @@ export class Broker {
342
343
  ...result,
343
344
  ...this.syncState(target),
344
345
  instructions:
345
- "Synchronization released. The surviving execution may continue the current task. Executions with rejected or unavailable codes remain finished.",
346
+ "Synchronization released. Continue as directed by the coordinator.",
346
347
  };
347
348
  }
348
349
 
@@ -809,12 +810,17 @@ export class Broker {
809
810
  event: "joined",
810
811
  initialization: explicit ? "explicit" : "implicit",
811
812
  });
813
+ const name = activity.requestId?.match(/\/([^/]+)$/)?.[1];
814
+ const instructions = name
815
+ ? `${historyInstructions} Use this name when coordinating through chat.`
816
+ : historyInstructions;
812
817
  return {
813
818
  sessionId,
819
+ ...(name ? { name } : {}),
814
820
  ...(code ? { code } : {}),
815
821
  instructions: code
816
- ? `${historyInstructions} Retain this initialization code in this ChatGPT context for sync verification; Pi history and chat messages contain only coordination details.`
817
- : historyInstructions,
822
+ ? `${instructions} Keep this code private for sync verification.`
823
+ : instructions,
818
824
  };
819
825
  }
820
826
 
@@ -990,7 +996,7 @@ export class Broker {
990
996
  }
991
997
 
992
998
  function syncMessage(sessionId: string): string {
993
- return `Pi session ${sessionId} is synchronizing to end duplicate executions. Ordinary tools and initialization are locked for this session and its bound ChatGPT conversations. Verify with the code returned by your own most recent initialization. With a missing or rejected code, this execution is an accidental duplicate: ${exitInstructions} The verified execution must confirm the conflicting executions' final exits before releasing the lock.`;
999
+ return `Pi session ${sessionId} is synchronizing. Ordinary tools and initialization are locked. Verify with your own initialization code; keep it private. ${coordinationInstructions} Only the verified coordinator can release.`;
994
1000
  }
995
1001
 
996
1002
  function abortError(signal: AbortSignal): Error {
@@ -2,6 +2,8 @@ Chappie connects this ChatGPT conversation to Pi sessions on one or more devices
2
2
 
3
3
  init sets this chat's default Pi session. An execution tool also establishes a default on first use: sessionId selects its target, or omitting it selects the first online session with no saved bindings. These sessions may already contain work. Once a default exists, another tool's sessionId selects only that call's target. Defaults survive broker restarts, and several chats can share one Pi session. When resuming work after explicit or implicit initialization, read recent history to recover progress, then continue from the current request.
4
4
 
5
+ Use initialization.name, derived from the initialization request suffix, to identify yourself during coordination.
6
+
5
7
  The active model is this existing ChatGPT conversation. A Pi tool that starts another chappie/chatgpt agent has no ChatGPT conversation to attach to and will wait indefinitely. Subagents targeting another configured model keep that provider's normal behavior.
6
8
 
7
9
  Prefer ChatGPT's web search, connectors, and cloud tools for remote research and cloud-side work. Use Chappie for local files, processes, Pi extensions, and Pi user interfaces. Pi project-memory tools operate on their local stores; Pi context-reduction tools do not change this ChatGPT conversation.
@@ -14,4 +16,4 @@ transfer pairs files from ChatGPT with Pi destination paths in order. Omit files
14
16
 
15
17
  Tool results identify the executing Pi sessionId and cwd. A shell command can access another directory without changing its Pi session. structuredContent.text includes the complete text, new Pi input, webAnswer, and deferred results; images and resources are native content blocks. Continue from received results rather than repeating work. Host deadlines include queueing and execution; use local persistent processes for longer work.
16
18
 
17
- Use history to read the current Pi branch with original entry IDs and timestamps, including chat activity records. Assistant messages and tool results expose their originating chatId and requestId in message.chappie; activity records include the same fields. Request IDs and the workflow suffix in notices describe recorded requests, and duplicate executions can share a workflow ID. It defaults to the last 20 entries; before pages backward and after pages forward. History is a record of past work, separate from new Pi input. It leaves a reading notice and returns its content only to the caller.
19
+ Use history to read the current Pi branch with original entry IDs and timestamps, including chat activity records. Assistant messages and tool results expose their originating chatId and requestId in message.chappie; activity records include the same fields. Parallel executions can share a chatId and workflow ID. Workflow timing helps explain differences in their task goals. History defaults to the last 20 entries; before pages backward and after pages forward. History is a record of past work, separate from new Pi input. It leaves a reading notice and returns its content only to the caller.
package/src/server.ts CHANGED
@@ -33,7 +33,7 @@ const outputSchema = z.object({
33
33
  });
34
34
 
35
35
  const syncInstructions =
36
- "Synchronization ends accidental duplicate executions so one execution continues. Retain each initialization code privately in this ChatGPT context for its Pi session. On conflicting activity, call sync with action start, then verify with the code returned by your own most recent initialization. A rejected or unavailable code means this execution must send one final chat message with its task, entry time, and explicit exit statement, then end this ChatGPT response immediately. It must not wait for unlock, poll, reinitialize, or resume after release. Verification returns a new code and leaves ordinary tools and initialization locked. The verified execution uses chat and history to identify conflicting executions, require their final exit messages, and confirm their responses have ended before releasing with that code. Idle Pi status or a quiet history page is not proof of exit. Codes belong only in initialization and sync exchanges, never in chat, history, or notes.";
36
+ "On conflicting activity, start sync and verify with your own initialization code. Keep codes private. Verification returns a new code to the coordinator; ordinary tools and initialization stay locked. Discuss through chat and history, including after failed verification. The coordinator decides who continues, their tasks, and who exits, including retaining only one execution. Acknowledge the decision; if asked to exit, leave a chat handoff and end your response. Only the coordinator releases after decisions and exits are confirmed.";
37
37
 
38
38
  const questionTemplate = "ui://chappie/question.html";
39
39
  const questionSchema = outputSchema.extend({ question: questionOutput });
@@ -89,7 +89,7 @@ export function createServer(broker: Broker): McpServer {
89
89
  {
90
90
  title: "Connect to Pi",
91
91
  description:
92
- "Select this chat's default Pi session and return its environment and tool catalog. Use the task's sessionId to resume, or find it by cwd/name with sessions. For a task without a specified target, omit sessionId to reuse the default or select the first online, unbound session. Read recent history when resuming work.",
92
+ "Select this chat's default Pi session and return its environment, tool catalog, and initialization name for coordination. Use the task's sessionId to resume, or find it by cwd/name with sessions. For a task without a specified target, omit sessionId to reuse the default or select the first online, unbound session. Read recent history when resuming work.",
93
93
  outputSchema,
94
94
  inputSchema: z.object({
95
95
  sessionId: z
@@ -467,7 +467,7 @@ export function createServer(broker: Broker): McpServer {
467
467
  {
468
468
  title: "Synchronize Pi activity",
469
469
  description:
470
- "End duplicate executions: start synchronization, verify your own latest initialization code, and release after conflicting executions exit. Failed verification requires a final chat exit message followed by ending this response. Verification leaves the session locked; chat and history support exit coordination.",
470
+ "Pause conflicting activity. Verify your initialization code to coordinate; failed verification still permits discussion. The coordinator decides who continues, their tasks, who exits, and when to release.",
471
471
  inputSchema: z.object({
472
472
  action: z.enum(["start", "verify", "release"]),
473
473
  code: z