@fungi.computer/graft 0.1.4 → 0.1.6

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.
Files changed (2) hide show
  1. package/dist/adapter.js +28 -19
  2. package/package.json +2 -2
package/dist/adapter.js CHANGED
@@ -33,14 +33,7 @@ function notify(client, sessionId, update) {
33
33
  update,
34
34
  });
35
35
  }
36
- /**
37
- * Return the part of `next` that extends what was sent. ACP chunks cannot be
38
- * retracted, so a revision that does not extend the sent text sends nothing.
39
- */
40
- function unsent(sent, next) {
41
- return next.startsWith(sent) ? next.slice(sent.length) : "";
42
- }
43
- async function projectMessage(client, sessionId, message, streamed = { text: "", thinking: "" }) {
36
+ async function projectMessage(client, sessionId, message, streamed = { text: 0, thinking: 0 }) {
44
37
  const value = message.value;
45
38
  if (value.role === "system")
46
39
  return;
@@ -53,13 +46,13 @@ async function projectMessage(client, sessionId, message, streamed = { text: "",
53
46
  return;
54
47
  }
55
48
  if (value.role === "assistant") {
56
- const thinking = unsent(streamed.thinking, thinkingFromAssistant(value.content));
49
+ const thinking = thinkingFromAssistant(value.content).slice(streamed.thinking);
57
50
  if (thinking)
58
51
  await notify(client, sessionId, {
59
52
  sessionUpdate: "agent_thought_chunk",
60
53
  content: acpText(thinking),
61
54
  });
62
- const text = unsent(streamed.text, textFromAssistant(value.content));
55
+ const text = textFromAssistant(value.content).slice(streamed.text);
63
56
  if (text)
64
57
  await notify(client, sessionId, {
65
58
  sessionUpdate: "agent_message_chunk",
@@ -95,14 +88,20 @@ async function projectProgress(client, sessionId, progress, turn) {
95
88
  if (turn.projected.has(progress.messageId))
96
89
  return;
97
90
  const sent = turn.streamed.get(progress.messageId) ?? {
98
- text: "",
99
- thinking: "",
91
+ text: 0,
92
+ thinking: 0,
100
93
  };
101
- const thinking = unsent(sent.thinking, progress.thinking);
102
- const text = unsent(sent.text, progress.text);
94
+ // ACP cannot retract text or represent a missing prefix. If a reset starts
95
+ // beyond what it has seen, leave that channel to exact durable completion.
96
+ const thinking = progress.thinking.offset > sent.thinking
97
+ ? ""
98
+ : progress.thinking.append.slice(Math.max(0, sent.thinking - progress.thinking.offset));
99
+ const text = progress.text.offset > sent.text
100
+ ? ""
101
+ : progress.text.append.slice(Math.max(0, sent.text - progress.text.offset));
103
102
  turn.streamed.set(progress.messageId, {
104
- text: sent.text + text,
105
- thinking: sent.thinking + thinking,
103
+ text: sent.text + text.length,
104
+ thinking: sent.thinking + thinking.length,
106
105
  });
107
106
  if (thinking)
108
107
  await notify(client, sessionId, {
@@ -140,10 +139,20 @@ async function projectFrame(client, sessionId, frame, turn) {
140
139
  return [];
141
140
  }
142
141
  if (frame.kind === "reset") {
143
- // A reset replaces the baseline mid-turn. Project this turn's messages
144
- // that were not already sent; everything else is history. Later progress
145
- // frames carry cumulative text, so live progress resumes from its delta.
142
+ // A reset replaces the baseline mid-turn. Completed messages fill any
143
+ // missing prefix; a non-truncated tail may resume the live appends too.
146
144
  await projectTurnMessages(client, sessionId, frame.baseline.messages, turn);
145
+ const progress = frame.baseline.progress;
146
+ if (progress !== null)
147
+ await projectProgress(client, sessionId, {
148
+ messageId: progress.messageId,
149
+ revision: progress.revision,
150
+ text: { offset: progress.textOffset, append: progress.text },
151
+ thinking: {
152
+ offset: progress.thinkingOffset,
153
+ append: progress.thinking,
154
+ },
155
+ }, turn);
147
156
  return frame.baseline.terminals;
148
157
  }
149
158
  await projectTurnMessages(client, sessionId, frame.messages, turn);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/graft",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "ACP v1 projection for an authorized Shiitake Session",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,7 +25,7 @@
25
25
  "@agentclientprotocol/sdk": "1.7.0",
26
26
  "@fungi.computer/cairn": "0.1.0",
27
27
  "@fungi.computer/mule": "0.2.1",
28
- "@fungi.computer/shiitake": "0.6.0"
28
+ "@fungi.computer/shiitake": "0.8.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "24.13.3",