@deepseek-harness-tui/dsh-tui 0.10.1 → 0.10.2

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 (74) hide show
  1. package/README.md +5 -146
  2. package/lib/types/adapter/ports/channel-session.d.ts +3 -1
  3. package/lib/types/adapter/ports/channel-session.d.ts.map +1 -1
  4. package/lib/types/components/Chat/JobCard.d.ts.map +1 -1
  5. package/lib/types/components/Chat/JobCard.js +8 -1
  6. package/lib/types/components/Markdown.d.ts +2 -2
  7. package/lib/types/components/Markdown.d.ts.map +1 -1
  8. package/lib/types/components/Markdown.js +28 -3
  9. package/lib/types/components/MessageList.d.ts.map +1 -1
  10. package/lib/types/components/MessageList.js +10 -9
  11. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  12. package/lib/types/components/StreamingMarkdown.js +61 -9
  13. package/lib/types/dsh-adapter/channel/binding-events.d.ts +5 -0
  14. package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
  15. package/lib/types/dsh-adapter/channel/binding-events.js +12 -2
  16. package/lib/types/dsh-adapter/channel/input-delivery.d.ts +5 -2
  17. package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
  18. package/lib/types/dsh-adapter/channel/input-delivery.js +71 -7
  19. package/lib/types/dsh-adapter/channel/permissions.d.ts.map +1 -1
  20. package/lib/types/dsh-adapter/channel/permissions.js +10 -7
  21. package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -1
  22. package/lib/types/dsh-adapter/channel/session-rewind.js +9 -2
  23. package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -1
  24. package/lib/types/dsh-adapter/channel/session-tree-actions.js +9 -3
  25. package/lib/types/dsh-adapter/channel/skill-catalog.d.ts +8 -2
  26. package/lib/types/dsh-adapter/channel/skill-catalog.d.ts.map +1 -1
  27. package/lib/types/dsh-adapter/channel/skill-catalog.js +49 -17
  28. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  29. package/lib/types/dsh-adapter/channel.js +5 -2
  30. package/lib/types/dsh-adapter/compat/liveSession.d.ts +5 -3
  31. package/lib/types/dsh-adapter/compat/liveSession.d.ts.map +1 -1
  32. package/lib/types/dsh-adapter/compat/liveSession.js +5 -3
  33. package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -1
  34. package/lib/types/dsh-adapter/sessions/digest.js +40 -19
  35. package/lib/types/dsh-adapter/sessions/frames.js +2 -0
  36. package/lib/types/dsh-adapter/sessions/list.js +3 -3
  37. package/lib/types/dsh-adapter/sessions/store.d.ts +2 -2
  38. package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -1
  39. package/lib/types/dsh-adapter/sessions/store.js +12 -11
  40. package/lib/types/dsh-adapter/sessions/types.d.ts +2 -0
  41. package/lib/types/dsh-adapter/sessions/types.d.ts.map +1 -1
  42. package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
  43. package/lib/types/ink/components/ScrollBox.js +30 -33
  44. package/lib/types/ink/dom.d.ts.map +1 -1
  45. package/lib/types/ink/dom.js +32 -64
  46. package/lib/types/ink/get-max-width.d.ts +7 -15
  47. package/lib/types/ink/get-max-width.d.ts.map +1 -1
  48. package/lib/types/ink/get-max-width.js +7 -20
  49. package/lib/types/ink/ink.d.ts +1 -0
  50. package/lib/types/ink/ink.d.ts.map +1 -1
  51. package/lib/types/ink/ink.js +22 -3
  52. package/lib/types/ink/layout/node.d.ts +2 -0
  53. package/lib/types/ink/layout/node.d.ts.map +1 -1
  54. package/lib/types/ink/layout/yoga.d.ts +1 -0
  55. package/lib/types/ink/layout/yoga.d.ts.map +1 -1
  56. package/lib/types/ink/layout/yoga.js +3 -0
  57. package/lib/types/ink/node-cache.d.ts +11 -0
  58. package/lib/types/ink/node-cache.d.ts.map +1 -1
  59. package/lib/types/ink/node-cache.js +3 -0
  60. package/lib/types/ink/output.d.ts +6 -1
  61. package/lib/types/ink/output.d.ts.map +1 -1
  62. package/lib/types/ink/output.js +55 -65
  63. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  64. package/lib/types/ink/render-node-to-output.js +92 -67
  65. package/lib/types/ink/tabstops.d.ts +3 -2
  66. package/lib/types/ink/tabstops.d.ts.map +1 -1
  67. package/lib/types/ink/tabstops.js +5 -4
  68. package/lib/types/ink/wrap-text.js +2 -2
  69. package/lib/types/native-ts/yoga-layout/index.d.ts +8 -3
  70. package/lib/types/native-ts/yoga-layout/index.d.ts.map +1 -1
  71. package/lib/types/native-ts/yoga-layout/index.js +23 -8
  72. package/lib/types/screens/Chat.d.ts.map +1 -1
  73. package/lib/types/screens/Chat.js +20 -8
  74. package/package.json +3 -2
@@ -14,6 +14,59 @@ export function createInputDelivery(ctx, owner, binding, state, notify, trackPen
14
14
  * through this chain to keep the send order FIFO.
15
15
  */
16
16
  let inputChain = Promise.resolve();
17
+ /**
18
+ * Attached-context registry (issue #842): `deliverUserText(..., attach)`
19
+ * records the model-facing companion message under the DELIVERED user
20
+ * message's id. The resident `agent/pre-step` listener below claims an entry
21
+ * exactly once — on the step that admits its user message — and appends it
22
+ * AFTER `next()`'s batch, the same shape and order as dsh-tool-skill's
23
+ * gesture boundary. The map is emptied on claim, on inbox discard and on
24
+ * channel release (owner.own below).
25
+ */
26
+ const attachedByMessageId = new Map();
27
+ /** Claim the batch's registered attachments, deleting each entry so a later
28
+ * step can never append it a second time. */
29
+ const claimAttachments = (messages) => {
30
+ if (attachedByMessageId.size === 0)
31
+ return [];
32
+ const attached = [];
33
+ for (const message of messages) {
34
+ const context = attachedByMessageId.get(message.id);
35
+ if (context === undefined)
36
+ continue;
37
+ attachedByMessageId.delete(message.id);
38
+ attached.push(context);
39
+ }
40
+ return attached;
41
+ };
42
+ /** Drop an attachment whose user message will never be claimed (inbox discard). */
43
+ const retireAttachment = (messageId) => {
44
+ attachedByMessageId.delete(messageId);
45
+ };
46
+ /**
47
+ * Resident `agent/pre-step` listener (D3/D4): `await next()` lets every
48
+ * later transform run first; the waterfall applies AFTER-next transforms in
49
+ * registration order, so registering at channel construction keeps this
50
+ * append LAST (范例: presets/liangshen/instruction-hint.mjs). A rejected
51
+ * step never claims — no context message without its admitted user line —
52
+ * and a released owner returns the decision untouched. One `owner.own`
53
+ * lifetime covers both the subscription and the whole registry (D5).
54
+ */
55
+ const disposePreStep = ctx.on('agent/pre-step', async (_payload, next) => {
56
+ const decision = await next();
57
+ if (decision.kind === 'reject')
58
+ return decision;
59
+ if (!owner.current())
60
+ return decision;
61
+ const attached = claimAttachments(decision.messages);
62
+ if (attached.length === 0)
63
+ return decision;
64
+ return { ...decision, messages: [...decision.messages, ...attached] };
65
+ });
66
+ owner.own(() => {
67
+ disposePreStep();
68
+ attachedByMessageId.clear();
69
+ });
17
70
  /** D-6 fence: the submission belongs to the session it was typed in. */
18
71
  const current = (origin) => owner.current() && binding.agent === origin.agent && state().agentBindingGeneration === origin.generation;
19
72
  /** D-6: bind the submission to the session it was typed in AT ENQUEUE
@@ -36,7 +89,7 @@ export function createInputDelivery(ctx, owner, binding, state, notify, trackPen
36
89
  * never the file dump) and each resolved reference appends a model-facing
37
90
  * attachment block. The pending preview tracks the typed text.
38
91
  */
39
- const deliverUserText = async (text, placement, images, origin) => {
92
+ const deliverUserText = async (text, placement, images, origin, attach) => {
40
93
  const orderedImages = orderedComposerImages(text, images, origin.stagedImages);
41
94
  // A `[Image #N]` placeholder whose staging was evicted (FIFO cap) or
42
95
  // whose draft capability was lost (history/rewind/session switch) would
@@ -58,6 +111,11 @@ export function createInputDelivery(ctx, owner, binding, state, notify, trackPen
58
111
  content: expansion.blocks,
59
112
  source: { kind: 'user' },
60
113
  });
114
+ // The message is real from here on: remember its attached context BEFORE
115
+ // the agent call so the pre-step listener can find it (D6). A throwing
116
+ // followup/steer rolls both the pending preview and this entry back.
117
+ if (attach !== undefined)
118
+ attachedByMessageId.set(message.id, attach);
61
119
  // Track BEFORE the agent call: a synchronous throw inside
62
120
  // followup/steer rolls the preview back; otherwise the inbox events
63
121
  // retire it once the message is claimed or discarded.
@@ -69,6 +127,8 @@ export function createInputDelivery(ctx, owner, binding, state, notify, trackPen
69
127
  origin.agent.followup(message);
70
128
  }
71
129
  catch (error) {
130
+ if (attach !== undefined)
131
+ attachedByMessageId.delete(message.id);
72
132
  untrackPending(message.id);
73
133
  throw error;
74
134
  }
@@ -156,7 +216,7 @@ export function createInputDelivery(ctx, owner, binding, state, notify, trackPen
156
216
  * stale text with a notice instead of sending the old conversation's
157
217
  * words to the new session.
158
218
  */
159
- const runUserTextDecision = async (text, placement, images, origin) => {
219
+ const runUserTextDecision = async (text, placement, images, origin, attach) => {
160
220
  // Stale detection compares the AGENT REFERENCE, not the id: session ids
161
221
  // are reusable (A → /new → /resume A lands back on the same id with a
162
222
  // fresh agent), so an id check has an ABA hole. Both origin values are
@@ -198,7 +258,7 @@ export function createInputDelivery(ctx, owner, binding, state, notify, trackPen
198
258
  text = decision.text.trim();
199
259
  }
200
260
  try {
201
- await deliverUserText(text, placement, images, origin);
261
+ await deliverUserText(text, placement, images, origin, attach);
202
262
  }
203
263
  catch (error) {
204
264
  // The FIFO must survive a failed expansion/send: surface it, then let
@@ -208,18 +268,20 @@ export function createInputDelivery(ctx, owner, binding, state, notify, trackPen
208
268
  notify(t('send-failed', { err: message }), { color: 'error' });
209
269
  }
210
270
  };
211
- const dispatchUserText = (text, placement, images = []) => {
271
+ const dispatchUserText = (text, placement, images = [], attach) => {
212
272
  const origin = captureOrigin();
213
273
  const capturedImages = composer.captureDraftImages(text, images);
214
- inputChain = inputChain.then(() => runUserTextDecision(text, placement, capturedImages, origin)).catch((error) => {
274
+ inputChain = inputChain.then(() => runUserTextDecision(text, placement, capturedImages, origin, attach)).catch((error) => {
215
275
  // The chain must survive a failed decision: log, then continue with
216
276
  // the next queued submission.
217
277
  ctx.logger.warn('dsh-tui: tui/input dispatch failed: %o', error);
218
278
  });
219
279
  };
220
280
  /** Public companion for callers that own a line but not a draft (skill
221
- * registrations): same decision pass and FIFO as a typed submit. */
222
- const deliverUserTextNow = (text, placement, images = []) => dispatchUserText(text, placement, images);
281
+ * registrations): same decision pass and FIFO as a typed submit. The
282
+ * optional `attach` is registered by `deliverUserText` only once its user
283
+ * message is real, then appended at the end of that message's step batch. */
284
+ const deliverUserTextNow = (text, placement, images = [], attach) => dispatchUserText(text, placement, images, attach);
223
285
  /** Main's `clearStagedImages`: revoke capabilities AND release the FIFO so
224
286
  * a task parked on the replaced session cannot wedge the new one, and drop
225
287
  * every pending-decision indicator owned by the session being replaced. */
@@ -232,6 +294,8 @@ export function createInputDelivery(ctx, owner, binding, state, notify, trackPen
232
294
  return {
233
295
  dispatchUserText,
234
296
  deliverUserText: deliverUserTextNow,
297
+ claimAttachments,
298
+ retireAttachment,
235
299
  withDecisionPending,
236
300
  clearStagedImages,
237
301
  stageImage: (input) => composer.stageImage(input),
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/permissions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAE5D,OAAO,KAAK,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAE3G,eAAO,MAAM,wBAAwB,WAAW,CAAA;AAEhD,eAAO,MAAM,4BAA4B,MAAM,CAAA;AAE/C,eAAO,MAAM,mCAAmC,MAAM,CAAA;AAEtD;;gFAEgF;AAChF,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACtE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IACnC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAClD;AAED,iDAAiD;AACjD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC,SAAS,CAAC,CAAA;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC,UAAU,CAAC,CAAA;CAChD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,uBAAuB,GAAG,SAAS,CAE7F;AAED;;iFAEiF;AACjF,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,wBAAwB,GAAG,wBAAwB,CAQ3G;AAED,wBAAgB,6BAA6B,IAAI,SAAS,sBAAsB,EAAE,CAkBjF;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,GAAG,wBAAwB,CAU5G;AAED,wBAAgB,mCAAmC,IAAI,wBAAwB,CAE9E;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,GAAG,SAAS,CAclG;AAED;;;0CAG0C;AAC1C,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,sBAAsB,EAAE,CAwBhG;AAED;;;wCAGwC;AACxC,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,GACf,wBAAwB,CAqE1B"}
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/permissions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAE5D,OAAO,KAAK,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAE3G,eAAO,MAAM,wBAAwB,WAAW,CAAA;AAEhD,eAAO,MAAM,4BAA4B,MAAM,CAAA;AAE/C,eAAO,MAAM,mCAAmC,MAAM,CAAA;AAEtD;;gFAEgF;AAChF,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACtE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IACnC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAClD;AAED,iDAAiD;AACjD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC,SAAS,CAAC,CAAA;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC,UAAU,CAAC,CAAA;CAChD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,uBAAuB,GAAG,SAAS,CAE7F;AAED;;iFAEiF;AACjF,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,wBAAwB,GAAG,wBAAwB,CAQ3G;AAED,wBAAgB,6BAA6B,IAAI,SAAS,sBAAsB,EAAE,CAkBjF;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,GAAG,wBAAwB,CAU5G;AAED,wBAAgB,mCAAmC,IAAI,wBAAwB,CAE9E;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,GAAG,SAAS,CAclG;AAED;;;0CAG0C;AAC1C,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,sBAAsB,EAAE,CAwBhG;AAED;;;wCAGwC;AACxC,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,GACf,wBAAwB,CAyE1B"}
@@ -80,16 +80,16 @@ export function normalizePermissionPresetOption(value) {
80
80
  * bundles (see `canonicalPresetFor`). */
81
81
  export function permissionBundlesFromService(service) {
82
82
  const runtime = permissionPresetRuntime(service);
83
- const resolve = runtime?.resolve;
84
- if (typeof resolve !== 'function')
83
+ if (runtime === undefined || typeof runtime.resolve !== 'function')
85
84
  return [];
86
- const names = Array.isArray(runtime?.names) ? runtime.names : [];
85
+ const names = Array.isArray(runtime.names) ? runtime.names : [];
87
86
  const bundles = [];
88
87
  for (const name of names) {
89
88
  if (typeof name !== 'string')
90
89
  continue;
91
90
  try {
92
- const spec = resolve(name);
91
+ // The registry resolves atoms on instance state; never detach `resolve`.
92
+ const spec = runtime.resolve(name);
93
93
  if (!isRecord(spec))
94
94
  continue;
95
95
  const sandbox = spec.sandbox;
@@ -115,12 +115,15 @@ export function permissionPresetSnapshotFromService(service, subject) {
115
115
  return unavailablePermissionPresetSnapshot();
116
116
  try {
117
117
  const capturedNames = Array.isArray(runtime.names) ? runtime.names : [];
118
- const current = runtime.current;
119
- const optionOf = runtime.optionOf;
120
118
  if (capturedNames.length === 0)
121
119
  return unavailablePermissionPresetSnapshot();
122
- if (typeof current !== 'function' || typeof optionOf !== 'function')
120
+ if (typeof runtime.current !== 'function' || typeof runtime.optionOf !== 'function') {
123
121
  return unavailablePermissionPresetSnapshot();
122
+ }
123
+ // Readback lives on the registry instance; bind so the calls below keep
124
+ // reaching it through the same object instead of a detached reference.
125
+ const current = runtime.current.bind(runtime);
126
+ const optionOf = runtime.optionOf.bind(runtime);
124
127
  const names = [];
125
128
  for (const name of capturedNames) {
126
129
  if (typeof name !== 'string' || name.trim() === '' || name === PERMISSION_PRESET_CUSTOM || names.includes(name)) {
@@ -1 +1 @@
1
- {"version":3,"file":"session-rewind.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-rewind.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAQvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEvD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAA;AAgB/E,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC,CAAA;IACjF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1K,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1F,IAEa,KAAK,OAAO,EAAE,OAAM,MAAM,GAAG,IAAW,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiFhF"}
1
+ {"version":3,"file":"session-rewind.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-rewind.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAQvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEvD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAA;AAgB/E,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC,CAAA;IACjF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1K,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1F,IAEa,KAAK,OAAO,EAAE,OAAM,MAAM,GAAG,IAAW,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwFhF"}
@@ -76,7 +76,14 @@ export function createRewindToAction(ctx, state, deps) {
76
76
  parentSession: deps.binding.agent.session.id,
77
77
  agentPreset: composed.agentPreset,
78
78
  agentOptions: { provider: state.provider, model: state.model },
79
- setup: composed.setup,
79
+ setup: async (agentCtx, agent) => {
80
+ // The cut keeps pre-turn inbox insertions but drops their claims.
81
+ // Newer hosts replay those inherited splices, so cancel the restored
82
+ // queue durably in the CHILD before publication or preset setup.
83
+ // Clearing only state.pending would hide, not revoke, the old work.
84
+ agent.inbox.clear();
85
+ return composed.setup?.(agentCtx, agent);
86
+ },
80
87
  })));
81
88
  }
82
89
  catch {
@@ -97,7 +104,7 @@ export function createRewindToAction(ctx, state, deps) {
97
104
  await deps.binding.abandon(handle);
98
105
  return null;
99
106
  }
100
- const sourceSessionId = deps.adoptForkedAgent(handle, adoption, seed, composed.agentPreset, childId);
107
+ const sourceSessionId = deps.adoptForkedAgent(handle, adoption, snapshotLiveSessionEvents(handle.agent.session), composed.agentPreset, childId);
101
108
  try {
102
109
  void dispatchTuiDecision(ctx, 'tui/rewind-done', {
103
110
  text: row.text,
@@ -1 +1 @@
1
- {"version":3,"file":"session-tree-actions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-tree-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AASvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,eAAe,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAA;AAYnF,+GAA+G;AAC/G,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC,CAAA;IACjF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1K,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACnG,IAEa,WAAW,MAAM,EAAE,KAAK,MAAM,EAAE,OAAM,QAAQ,GAAG,MAAiB,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwG1G"}
1
+ {"version":3,"file":"session-tree-actions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-tree-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AASvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,eAAe,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAA;AAYnF,+GAA+G;AAC/G,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC,CAAA;IACjF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1K,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACnG,IAEa,WAAW,MAAM,EAAE,KAAK,MAAM,EAAE,OAAM,QAAQ,GAAG,MAAiB,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4G1G"}
@@ -96,11 +96,17 @@ export function createTreeRewindAction(ctx, state, deps) {
96
96
  parentSession: SessionId(sessionId),
97
97
  agentPreset: composed.agentPreset,
98
98
  agentOptions: { provider: state.provider, model: state.model },
99
- setup: closeAfterCreate ? async (agentCtx, agent) => {
99
+ setup: closeAfterCreate || mode === 'rewind' ? async (agentCtx, agent) => {
100
100
  // V3 requires seed.length === inheritedEventCount. The constructor
101
101
  // inserts the inherited marker, then these closers belong to the
102
102
  // child and persist before publication, without falsifying the cut.
103
- closeLiveForkTurn(agent.session, target.closeTurn);
103
+ if (closeAfterCreate)
104
+ closeLiveForkTurn(agent.session, target.closeTurn);
105
+ // Re-editing starts with no historical pending work. Cancel through
106
+ // the child's Inbox so a later resume cannot resurrect the queue.
107
+ // A plain fork deliberately keeps its separate semantics.
108
+ if (mode === 'rewind')
109
+ agent.inbox.clear();
104
110
  return composed.setup?.(agentCtx, agent);
105
111
  } : composed.setup,
106
112
  })));
@@ -124,7 +130,7 @@ export function createTreeRewindAction(ctx, state, deps) {
124
130
  deps.notify(t('rewind-session-changed'), { color: 'error' });
125
131
  return null;
126
132
  }
127
- const replay = closeAfterCreate ? snapshotLiveSessionEvents(handle.agent.session) : seed;
133
+ const replay = closeAfterCreate || mode === 'rewind' ? snapshotLiveSessionEvents(handle.agent.session) : seed;
128
134
  const sourceSessionId = deps.adoptForkedAgent(handle, adoption, replay, composed.agentPreset, childId);
129
135
  deps.notifySessionSwitched(mode === 'fork' ? 'fork' : 'rewind', String(childId), sourceSessionId);
130
136
  return restoredText;
@@ -1,5 +1,6 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
2
  import type { Agent } from '@deepseek-ai/dsh-agent';
3
+ import { type UserMessage } from '@deepseek-ai/dsh-llm';
3
4
  import type { CommandRuntime } from '@deepseek-ai/dsh-commands';
4
5
  import { type LocalCommand, type LocalizedDescriptions } from '../../commands.js';
5
6
  import type { ChannelOwner } from './owner.js';
@@ -12,12 +13,17 @@ export declare function createSkillCatalog(ctx: Context, deps: {
12
13
  cwd(): string;
13
14
  setCommands(commands: LocalCommand[]): void;
14
15
  commandDescriptions(name: string): LocalizedDescriptions | undefined;
15
- deliverUserText(text: string, placement: 'followup'): void;
16
+ /** Submit a user line through the channel's delivery pipeline. The
17
+ * fallback skill path rides this same entry point and attaches the
18
+ * rendered body, so the line stays a plain user message (fence, pending
19
+ * preview and `@` expansion included) and the body is appended to that
20
+ * message's step batch — never a turn of its own. */
21
+ deliverUserText(text: string, placement: 'followup', attach?: UserMessage): void;
16
22
  }): {
17
23
  start: () => void;
18
24
  release: () => void;
19
25
  refreshCommands: () => void;
20
- refreshSkillCommands: () => Promise<void>;
26
+ refreshSkillCommands: (fromRetry?: boolean) => Promise<void>;
21
27
  registryFor: (agent: Agent) => SkillRegistry | undefined;
22
28
  viewOptions: (agent: Agent) => {
23
29
  scope: Agent;
@@ -1 +1 @@
1
- {"version":3,"file":"skill-catalog.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/skill-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAEnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAE/D,OAAO,EAA8E,KAAK,YAAY,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAI7J,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAIxD,oFAAoF;AACpF,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,CAAC,CAAA;IAC5C,cAAc,EAAE,cAAc,GAAG,SAAS,CAAA;IAC1C,KAAK,IAAI,KAAK,CAAA;IACd,GAAG,IAAI,MAAM,CAAA;IACb,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI,CAAA;IAC3C,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAAA;IACpE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,GAAG,IAAI,CAAA;CAC3D;iBA8HiB,IAAI;mBARF,IAAI;2BA3GI,IAAI;gCAkDO,OAAO,CAAC,IAAI,CAAC;yBApDxB,KAAK,KAAG,aAAa,GAAG,SAAS;yBADjC,KAAK,KAAG;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;EA0IlE"}
1
+ {"version":3,"file":"skill-catalog.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/skill-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAE/D,OAAO,EAA8E,KAAK,YAAY,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAI7J,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAKxD,oFAAoF;AACpF,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,CAAC,CAAA;IAC5C,cAAc,EAAE,cAAc,GAAG,SAAS,CAAA;IAC1C,KAAK,IAAI,KAAK,CAAA;IACd,GAAG,IAAI,MAAM,CAAA;IACb,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI,CAAA;IAC3C,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAAA;IACpE;;;;0DAIsD;IACtD,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;CACjF;iBA4JiB,IAAI;mBARF,IAAI;2BAhII,IAAI;mDAoDwB,OAAO,CAAC,IAAI,CAAC;yBAtDzC,KAAK,KAAG,aAAa,GAAG,SAAS;yBADjC,KAAK,KAAG;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;EA+JlE"}
@@ -5,13 +5,23 @@ import { t } from '../../i18n.js';
5
5
  import { serviceForAgent } from '../presets.js';
6
6
  import { hasCommandErrorCode, mapCommandError } from '../command-errors.js';
7
7
  const SKILL_COMMAND_RETRY_MS = 800;
8
+ const MAX_SKILL_RETRY_ATTEMPTS = 3;
8
9
  /** Owns the merged catalog, host registrations, retry timer and all skill reads. */
9
10
  export function createSkillCatalog(ctx, deps) {
10
11
  let commandListSeq = 0;
12
+ let skillCommandSeq = 0;
11
13
  let lastGoodSkills;
12
14
  const registrations = new Map();
13
15
  const refused = new Set();
14
16
  let retry;
17
+ let retryAttempts = 0;
18
+ let released = false;
19
+ const resetRetry = () => {
20
+ if (retry !== undefined)
21
+ clearTimeout(retry);
22
+ retry = undefined;
23
+ retryAttempts = 0;
24
+ };
15
25
  const viewOptions = (agent) => ({ scope: agent, cwd: deps.cwd() });
16
26
  const registryFor = (agent) => serviceForAgent(ctx, agent, 'skills');
17
27
  const refreshCommands = () => {
@@ -57,16 +67,18 @@ export function createSkillCatalog(ctx, deps) {
57
67
  restore();
58
68
  return;
59
69
  }
60
- const withSkills = [...merged];
70
+ // Cache the observation, not the delta: registered skills are already
71
+ // in merged on later reads. Local/host collisions still win on restore.
72
+ const skills = [];
61
73
  for (const skill of observation.skills) {
62
- if (isUserInvocable(skill) && !withSkills.some(command => command.name === skill.name)) {
63
- withSkills.push({ name: skill.name, description: skill.description, skill: true });
74
+ if (isUserInvocable(skill) && !skills.some(command => command.name === skill.name)) {
75
+ skills.push({ name: skill.name, description: skill.description, skill: true });
64
76
  }
65
77
  }
66
- const added = withSkills.slice(merged.length);
67
- lastGoodSkills = { agent: target, commands: added };
78
+ lastGoodSkills = { agent: target, commands: skills };
79
+ const added = skills.filter(entry => !merged.some(command => command.name === entry.name));
68
80
  if (added.length > 0 && deps.owner.current() && target === deps.agent())
69
- deps.setCommands(withSkills);
81
+ deps.setCommands([...merged, ...added]);
70
82
  }).catch((error) => {
71
83
  if (!deps.owner.current() || token !== commandListSeq || target !== deps.agent())
72
84
  return;
@@ -74,9 +86,14 @@ export function createSkillCatalog(ctx, deps) {
74
86
  restore();
75
87
  });
76
88
  };
77
- const refreshSkillCommands = async () => {
78
- if (!deps.owner.current() || deps.commandService === undefined)
89
+ const refreshSkillCommands = async (fromRetry = false) => {
90
+ if (released || !deps.owner.current() || deps.commandService === undefined)
79
91
  return;
92
+ // Explicit refreshes (including skills/change and agent swaps) supersede
93
+ // the old ladder. Only the timer continuation consumes the same budget.
94
+ if (!fromRetry)
95
+ resetRetry();
96
+ const token = ++skillCommandSeq;
80
97
  const target = deps.agent();
81
98
  const registry = registryFor(target);
82
99
  if (registry === undefined)
@@ -86,15 +103,27 @@ export function createSkillCatalog(ctx, deps) {
86
103
  observation = await registry.snapshot(viewOptions(target));
87
104
  }
88
105
  catch (error) {
89
- if (deps.owner.current())
106
+ if (deps.owner.current() && token === skillCommandSeq && target === deps.agent())
90
107
  ctx.logger.warn('skill commands: catalog read failed: %o', error);
91
108
  return;
92
109
  }
93
- if (!deps.owner.current() || target !== deps.agent())
110
+ if (!deps.owner.current() || token !== skillCommandSeq || target !== deps.agent())
111
+ return;
112
+ if (!observation.complete) {
113
+ if (retry === undefined && retryAttempts < MAX_SKILL_RETRY_ATTEMPTS) {
114
+ const delay = SKILL_COMMAND_RETRY_MS * 2 ** retryAttempts++;
115
+ retry = setTimeout(() => {
116
+ retry = undefined;
117
+ if (token === skillCommandSeq && target === deps.agent())
118
+ void refreshSkillCommands(true);
119
+ }, delay);
120
+ retry.unref?.();
121
+ }
122
+ // Partial reads cannot revoke callable handlers or replace descriptions.
123
+ // Keep the last-good registrations until an authoritative observation.
94
124
  return;
95
- if (!observation.complete && retry === undefined) {
96
- retry = setTimeout(() => { retry = undefined; void refreshSkillCommands(); }, SKILL_COMMAND_RETRY_MS);
97
125
  }
126
+ resetRetry();
98
127
  const wanted = new Map(observation.skills
99
128
  .filter(isUserInvocable)
100
129
  .filter(skill => parseCommandName(`/${skill.name}`)?.name === skill.name)
@@ -129,7 +158,12 @@ export function createSkillCatalog(ctx, deps) {
129
158
  return { kind: 'error', text: t('skill-unavailable', { name }) };
130
159
  if (!deps.owner.current() || invoker !== deps.agent())
131
160
  return { kind: 'error', text: t('skill-unavailable', { name }) };
132
- invoker.followup(createUserMessage({ content: [{ type: 'text', text: renderSkillContent(skill) }], source: { kind: 'skill-invocation', name, form: 'instructions' } }));
161
+ // No `skill` tool: deliver the gesture as the user's own line and
162
+ // attach the rendered body. The channel's resident pre-step
163
+ // listener appends the body AFTER the admitted batch — the same
164
+ // shape/order as dsh-tool-skill's gesture boundary (#842).
165
+ const bodyMessage = createUserMessage({ content: [{ type: 'text', text: renderSkillContent(skill) }], source: { kind: 'skill-invocation', name, form: 'instructions' } });
166
+ deps.deliverUserText(`/${name}${rawInput}`, 'followup', bodyMessage);
133
167
  return { kind: 'success' };
134
168
  },
135
169
  });
@@ -144,14 +178,12 @@ export function createSkillCatalog(ctx, deps) {
144
178
  }
145
179
  }
146
180
  };
147
- let released = false;
148
181
  const release = () => {
149
182
  if (released)
150
183
  return;
151
184
  released = true;
152
- if (retry !== undefined)
153
- clearTimeout(retry);
154
- retry = undefined;
185
+ skillCommandSeq += 1;
186
+ resetRetry();
155
187
  for (const entry of registrations.values())
156
188
  entry.dispose();
157
189
  registrations.clear();
@@ -1 +1 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/channel.ts"],"names":[],"mappings":"AAUA,OAAO,EAA4B,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AA0BxF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,KAAK,KAAK,EAAqE,MAAM,wBAAwB,CAAA;AAuCtH,OAAO,KAAK,EAAyE,YAAY,EAA8T,MAAM,oBAAoB,CAAA;AAgCzb,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAkBnD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,OAAO,EACZ,YAAY,EAAE,KAAK,EACnB,OAAO,EAAE,oBAAoB,GAC5B,YAAY,CAUd;AAk3BD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,YAAY,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACryB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/channel.ts"],"names":[],"mappings":"AAUA,OAAO,EAA4B,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AA0BxF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,KAAK,KAAK,EAAqE,MAAM,wBAAwB,CAAA;AAwCtH,OAAO,KAAK,EAAyE,YAAY,EAA8T,MAAM,oBAAoB,CAAA;AAgCzb,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAkBnD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,OAAO,EACZ,YAAY,EAAE,KAAK,EACnB,OAAO,EAAE,oBAAoB,GAC5B,YAAY,CAUd;AAs3BD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,YAAY,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACryB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA"}
@@ -235,7 +235,7 @@ function createChannelWithOwner(ctx, initialAgent, options, owner) {
235
235
  const { warning: contextWarning, resetContextWarning, checkContextWarning, trackPending, untrackPending } = bookkeeping;
236
236
  const composer = createComposerImages(ctx, owner, { generation: () => state.agentBindingGeneration });
237
237
  const inputDelivery = createInputDelivery(ctx, owner, binding, () => state, (...args) => notify(...args), trackPending, untrackPending, composer);
238
- const { dispatchUserText, deliverUserText, withDecisionPending, clearStagedImages } = inputDelivery;
238
+ const { dispatchUserText, deliverUserText, retireAttachment, withDecisionPending, clearStagedImages } = inputDelivery;
239
239
  /**
240
240
  * The `tui/session-switch` decision event (pi's `session_before_switch`),
241
241
  * fired before `/new` or `/resume` replaces the live session (rewind has
@@ -475,7 +475,9 @@ function createChannelWithOwner(ctx, initialAgent, options, owner) {
475
475
  cwd: () => state.cwd,
476
476
  setCommands(commands) { state.commandList = commands; state.emit(); },
477
477
  commandDescriptions: name => commandTrees?.descriptions(name),
478
- deliverUserText,
478
+ // Attached-context pass-through (T03 consumes the third parameter in the
479
+ // fallback branch): the skill catalog never loses the FIFO/decision fence.
480
+ deliverUserText: (text, placement, attach) => deliverUserText(text, placement, [], attach),
479
481
  });
480
482
  const skillViewOptions = skillCatalog.viewOptions;
481
483
  const skillRegistryFor = skillCatalog.registryFor;
@@ -649,6 +651,7 @@ function createChannelWithOwner(ctx, initialAgent, options, owner) {
649
651
  subagents: subagentProjection,
650
652
  agentView,
651
653
  messageObserver,
654
+ retireAttachment,
652
655
  });
653
656
  const bindAgent = bindingEvents.bind;
654
657
  const sessionAdoption = createSessionAdoption(state, {
@@ -47,9 +47,11 @@ export interface LiveSessionSeedMetadata {
47
47
  * Create-time seed ownership fields. Every copied source prefix is inherited
48
48
  * state for domain projections, even when `/fork` deliberately omits
49
49
  * `parentSession` so the copy is presented as an independent root. The exact
50
- * cut prevents schedule/inbox/subagent projections from replaying copied
51
- * history as child-owned events. A child snapshot length cannot reliably be
52
- * used as that cut because construction may append `session/end-seed`.
50
+ * cut distinguishes copied history from child-owned events. Not every
51
+ * projection skips inherited events: newer inbox folds restore pending input
52
+ * from them, so rewind must cancel that work through the child's Inbox API.
53
+ * A child snapshot length cannot reliably be used as the cut because
54
+ * construction may append `session/end-seed`.
53
55
  */
54
56
  export declare function liveSessionSeedMetadata(session: unknown, inheritedCount: number): LiveSessionSeedMetadata;
55
57
  /** Close an open turn with the `turn/end` shape a real user cancellation writes. */
@@ -1 +1 @@
1
- {"version":3,"file":"liveSession.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/compat/liveSession.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAoChF;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,YAAY,EAAE,CAWnF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAI1D;AAMD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQlF;AAED,iFAAiF;AACjF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG;IAC1D,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;CACxC,CAcA;AASD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CA4BxF;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;QAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAC5B,CAAA;IACD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CACtC;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,GACrB,uBAAuB,CAQzB;AAED,oFAAoF;AACpF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CASjF;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAoBtE;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,YAAY,EAAE,CAAA;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAA;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAC,cAAc,CAAC,CAAA;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;CAC7C;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,wBAAwB,GAAG,kBAAkB,CAiB9F"}
1
+ {"version":3,"file":"liveSession.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/compat/liveSession.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAoChF;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,YAAY,EAAE,CAWnF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAI1D;AAMD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQlF;AAED,iFAAiF;AACjF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG;IAC1D,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;CACxC,CAcA;AASD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CA4BxF;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;QAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAC5B,CAAA;IACD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CACtC;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,GACrB,uBAAuB,CAQzB;AAED,oFAAoF;AACpF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CASjF;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAoBtE;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,YAAY,EAAE,CAAA;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAA;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAC,cAAc,CAAC,CAAA;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;CAC7C;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,wBAAwB,GAAG,kBAAkB,CAiB9F"}
@@ -148,9 +148,11 @@ export function sliceLiveSessionSeed(session, boundary) {
148
148
  * Create-time seed ownership fields. Every copied source prefix is inherited
149
149
  * state for domain projections, even when `/fork` deliberately omits
150
150
  * `parentSession` so the copy is presented as an independent root. The exact
151
- * cut prevents schedule/inbox/subagent projections from replaying copied
152
- * history as child-owned events. A child snapshot length cannot reliably be
153
- * used as that cut because construction may append `session/end-seed`.
151
+ * cut distinguishes copied history from child-owned events. Not every
152
+ * projection skips inherited events: newer inbox folds restore pending input
153
+ * from them, so rewind must cancel that work through the child's Inbox API.
154
+ * A child snapshot length cannot reliably be used as the cut because
155
+ * construction may append `session/end-seed`.
154
156
  */
155
157
  export function liveSessionSeedMetadata(session, inheritedCount) {
156
158
  if (usesIsSeeded(session)) {
@@ -1 +1 @@
1
- {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/sessions/digest.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3E,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,QAAY,CAAA;AAC1C,+EAA+E;AAC/E,eAAO,MAAM,eAAe,MAAM,CAAA;AAClC,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,QAAa,CAAA;AAqG3C;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CA+CtE;AAID,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;IACxC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAC7B;AA2GD,sEAAsE;AACtE,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAgCjE;AAyCD;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,oBAAoB,CAAC,CAS/B;AAED,iFAAiF;AACjF,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiB7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAyB1E"}
1
+ {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/sessions/digest.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3E,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,QAAY,CAAA;AAC1C,+EAA+E;AAC/E,eAAO,MAAM,eAAe,MAAM,CAAA;AAClC,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,QAAa,CAAA;AAwG3C;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAyDtE;AAID,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;IACxC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAC7B;AA2GD,sEAAsE;AACtE,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAgCjE;AA8CD;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,oBAAoB,CAAC,CAS/B;AAED,iFAAiF;AACjF,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiB7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAyB1E"}