dsh-thoughtdag 0.4.16 → 0.4.17

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 (20) hide show
  1. package/dist-app/assets/{canonical-ChxvAi6N.js → canonical-D4SquoNo.js} +2 -2
  2. package/dist-app/assets/{canvas-record-Be2aKIf8.js → canvas-record--EHyNDCM.js} +1 -1
  3. package/dist-app/assets/{claude-code-session-B51keZPd.js → claude-code-session-CJTZw-s-.js} +1 -1
  4. package/dist-app/assets/{codex-session-BR_x_Fzf.js → codex-session-kzdkETJK.js} +1 -1
  5. package/dist-app/assets/{dsh-session-D0yZa0_o.js → dsh-session-DrnTTQ3v.js} +1 -1
  6. package/dist-app/assets/{experiment-loop-pAs6MwPV.js → experiment-loop-BysOJBTB.js} +1 -1
  7. package/dist-app/assets/{index-JMt9ljo3.js → index-C1uc774A.js} +11 -11
  8. package/dist-app/assets/{index-DvBTIsHr.js → index-DE_CLHc3.js} +1 -1
  9. package/dist-app/assets/{index-_B6z8_Ff.js → index-DMEJ2pfg.js} +3 -3
  10. package/dist-app/assets/{index-962V9CYS.js → index-k_sPEfl2.js} +1 -1
  11. package/dist-app/assets/{live-mirror-C9sTXAvv.js → live-mirror-CkSO5X2s.js} +2 -2
  12. package/dist-app/assets/{pi-session-B7frsRRp.js → pi-session-CIlDUNBG.js} +1 -1
  13. package/dist-app/assets/{sensitive-scan-Cwg4RAEk.js → sensitive-scan-OXO0A60C.js} +1 -1
  14. package/dist-app/assets/{session-handoff-BXosSY-e.js → session-handoff-DF4fnjEz.js} +2 -2
  15. package/dist-app/assets/{shared-L3KzXorg.js → shared-ClPLXGBi.js} +1 -1
  16. package/dist-app/assets/{turndown-plugin-gfm.cjs-pG4f1F8S.js → turndown-plugin-gfm.cjs-DeGwvSez.js} +1 -1
  17. package/dist-app/assets/{update-check-lHz7ROW6.js → update-check-CNsM71E0.js} +1 -1
  18. package/dist-app/index.html +1 -1
  19. package/lib/index.js +34 -6
  20. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -639,6 +639,9 @@ function installApprovalAnswerer(ctx) {
639
639
  ctx.on('approval/request', async function (req, next) {
640
640
  const entry = canvasTurns.get(req?.agent?.id)
641
641
  if (!entry) return next()
642
+ // an approval raised by another turn of the same session (the person's
643
+ // own tool call, running before or after ours) stays with the harness UI
644
+ if (entry.turn === null || entry.current() !== entry.turn) return next()
642
645
  const id = 'td-' + randomUUID()
643
646
  const call = req.callId !== undefined ? entry.calls.get(req.callId) : undefined
644
647
  const name = call?.name ?? req.toolName
@@ -698,7 +701,9 @@ async function runAgentTurn(ctx, body, emit, isClosed, { answerApprovals = true
698
701
  emit({ harnessSession: sessionId, continued: sessionId === continueId, ...(forkedFrom ? { forkedFrom } : {}) })
699
702
  // a streaming caller can show and answer approvals; a one-shot caller
700
703
  // (/claude) cannot, so its requests fall through to the harness's own panel
701
- const turnEntry = { emit, isClosed, calls: new Map() }
704
+ // turn/current are filled once our message enters the session: the approval
705
+ // answerer uses them to take only the approvals our turn raises
706
+ const turnEntry = { emit, isClosed, calls: new Map(), turn: null, current: () => null }
702
707
  if (answerApprovals) canvasTurns.set(sessionId, turnEntry)
703
708
  const hasImages = Array.isArray(body?.images) && body.images.length > 0
704
709
  if (hasImages) {
@@ -714,17 +719,40 @@ async function runAgentTurn(ctx, body, emit, isClosed, { answerApprovals = true
714
719
  let fullText = ''
715
720
  let done
716
721
  const finished = new Promise(resolve => { done = resolve })
722
+ // The session is shared with the person chatting in the harness: their
723
+ // turns may run before, after or around ours (ours is queued behind
724
+ // whatever is running). Everything below is scoped to OUR turn. dsh stamps
725
+ // the prompt's requestId on the user message it creates (source.rpcId), so
726
+ // that message is recognised exactly; it names our turn, and only that
727
+ // turn's text, tool calls, approvals and end belong to this node (#42).
728
+ const requestId = 'td-' + randomUUID()
717
729
  let currentTurn = null
730
+ let ourTurn = null
731
+ let ourMessagePending = false
732
+ const claimTurn = (turn) => { ourTurn = turn; turnEntry.turn = turn }
733
+ const isOurs = (event) => ourTurn !== null && (typeof event.data?.turn === 'number' ? event.data.turn === ourTurn : currentTurn === ourTurn)
734
+ turnEntry.current = () => currentTurn
718
735
  const off = ctx.on('session/event', (session, event) => {
719
736
  if (session?.id !== sessionId || !event) return
720
- if (event.type === 'turn/start') { currentTurn = event.data?.turn ?? null; return }
737
+ if (event.type === 'turn/start') {
738
+ currentTurn = event.data?.turn ?? null
739
+ if (ourMessagePending && currentTurn !== null) { ourMessagePending = false; claimTurn(currentTurn) }
740
+ return
741
+ }
721
742
  // the person's question entering the surface: THIS is the turn the
722
743
  // canvas node stands for — with its id the canvas marks the node as the
723
- // mirror of that turn, and the live mirror does not append it a second time
724
- if (event.type === 'user/message' && (event.data?.source?.kind ?? 'user') === 'user') {
725
- emit({ harnessTurn: { session: sessionId, turn: currentTurn, userMessageId: event.data?.id ?? null, seq: event.seq ?? null } })
744
+ // mirror of that turn, and the live mirror does not append it a second
745
+ // time. Only our own message counts; a message the person typed in the
746
+ // chat meanwhile belongs to their turn, not to this node.
747
+ if (event.type === 'user/message') {
748
+ const source = event.data?.source
749
+ if (ourTurn === null && !ourMessagePending && (source?.kind ?? 'user') === 'user' && source?.rpcId === requestId) {
750
+ if (currentTurn === null) ourMessagePending = true; else claimTurn(currentTurn)
751
+ emit({ harnessTurn: { session: sessionId, turn: currentTurn, userMessageId: event.data?.id ?? null, seq: event.seq ?? null } })
752
+ }
726
753
  return
727
754
  }
755
+ if (!isOurs(event)) return
728
756
  if (event.type === 'assistant/chunk') {
729
757
  const c = event.data?.chunk
730
758
  if (c?.type === 'text-delta' && c.text) { sawChunk = true; fullText += c.text; emit({ text: c.text }) }
@@ -748,7 +776,7 @@ async function runAgentTurn(ctx, body, emit, isClosed, { answerApprovals = true
748
776
  for (const img of Array.isArray(body?.images) ? body.images : []) {
749
777
  if (img && typeof img.data === 'string' && IMAGE_MEDIA.has(img.mimeType)) promptContent.push({ type: 'image', mediaType: img.mimeType, data: img.data })
750
778
  }
751
- await ctx.sessionController.prompt({ requestId: 'td-' + randomUUID(), sessionId, mode: 'queue', content: promptContent }, AbortSignal.timeout(30000))
779
+ await ctx.sessionController.prompt({ requestId, sessionId, mode: 'queue', content: promptContent }, AbortSignal.timeout(30000))
752
780
  const timeout = new Promise(resolve => setTimeout(resolve, 20 * 60 * 1000))
753
781
  const closed = new Promise(resolve => { const t = setInterval(() => { if (isClosed()) { clearInterval(t); resolve() } }, 500); finished.then(() => clearInterval(t)) })
754
782
  await Promise.race([finished, agent.whenIdle().then(() => finished), timeout, closed])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-thoughtdag",
3
- "version": "0.4.16",
3
+ "version": "0.4.17",
4
4
  "description": "ThoughtDAG for DeepSeek Harness: turn the current DSH session into an editable thought graph on an infinite canvas.",
5
5
  "author": "Xia Chen",
6
6
  "repository": {