@pikiloom/kernel 0.2.16 → 0.2.18

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.
@@ -57,6 +57,7 @@ export declare function emitClaudeImages(blocks: any[], s: any, emit: (e: Driver
57
57
  export declare function todoWriteToPlan(input: any): UniversalPlan | null;
58
58
  export declare function readClaudeTaskCreateId(ev: any, block: any): string | null;
59
59
  export declare function rebuildClaudeTaskPlan(s: any): UniversalPlan | null;
60
+ export declare function applyTaskUpdateToTodoPlan(plan: UniversalPlan | null | undefined, taskId: string, rawStatus: string): UniversalPlan | null;
60
61
  export declare function shortToolValue(value: unknown, max?: number): string;
61
62
  export declare function summarizeToolUse(name: string, input: any): string;
62
63
  export declare function firstResultLine(content: any): string;
@@ -531,29 +531,41 @@ export function handleClaudeEvent(ev, s, emit) {
531
531
  const name = String(b.name || 'Tool');
532
532
  if (name === 'TodoWrite') {
533
533
  const plan = todoWriteToPlan(b.input);
534
- if (plan)
534
+ if (plan) {
535
+ s.todoPlan = plan;
535
536
  emit({ type: 'plan', plan });
537
+ }
538
+ const summary = 'Update plan';
539
+ (s.tools ||= new Map()).set(id, { name, summary });
540
+ emit({ type: 'tool', call: { id, name, summary, input: null, status: 'running' } });
536
541
  continue;
537
542
  }
538
543
  // Task list (current Claude mechanism): stash the subject; the tool_result assigns the id.
539
- // Plan-only like the legacy driver these never surface as Activity rows.
544
+ // The command surfaces as an Activity row (matching the CLI's own transcript); the
545
+ // structured task state still flows through plan events.
540
546
  if (name === 'TaskCreate') {
541
547
  const subject = typeof b.input?.subject === 'string' ? b.input.subject.trim() : '';
542
548
  if (subject)
543
549
  (s.pendingTaskCreates ||= new Map()).set(id, { subject });
550
+ const summary = subject ? `Create task: ${subject}` : 'Create task';
551
+ (s.tools ||= new Map()).set(id, { name, summary });
552
+ emit({ type: 'tool', call: { id, name, summary, input: null, status: 'running' } });
544
553
  continue;
545
554
  }
546
555
  if (name === 'TaskUpdate') {
547
556
  const taskId = String(b.input?.taskId ?? '').trim();
548
557
  const rawStatus = String(b.input?.status ?? '').trim().toLowerCase();
549
- if (taskId) {
558
+ // Chronological: the plan reflects the state AFTER this update. An id known to the
559
+ // TaskCreate store lands there; otherwise it lands positionally on the latest
560
+ // TodoWrite list (ids are 1-based positions when the todo panel owns the list).
561
+ if (taskId && s.taskList?.has(taskId)) {
550
562
  if (rawStatus === 'deleted') {
551
- s.taskList?.delete(taskId);
563
+ s.taskList.delete(taskId);
552
564
  if (Array.isArray(s.taskOrder))
553
565
  s.taskOrder = s.taskOrder.filter((x) => x !== taskId);
554
566
  }
555
567
  else if (rawStatus) {
556
- const existing = s.taskList?.get(taskId);
568
+ const existing = s.taskList.get(taskId);
557
569
  if (existing)
558
570
  existing.status = rawStatus;
559
571
  }
@@ -561,6 +573,16 @@ export function handleClaudeEvent(ev, s, emit) {
561
573
  if (plan)
562
574
  emit({ type: 'plan', plan });
563
575
  }
576
+ else if (taskId) {
577
+ const updated = applyTaskUpdateToTodoPlan(s.todoPlan, taskId, rawStatus);
578
+ if (updated) {
579
+ s.todoPlan = updated;
580
+ emit({ type: 'plan', plan: updated });
581
+ }
582
+ }
583
+ const summary = `Update task ${taskId || '?'} → ${rawStatus || 'unknown'}`;
584
+ (s.tools ||= new Map()).set(id, { name, summary });
585
+ emit({ type: 'tool', call: { id, name, summary, input: null, status: 'running' } });
564
586
  continue;
565
587
  }
566
588
  if (name === 'Task' || name === 'Agent') {
@@ -641,13 +663,16 @@ export function handleClaudeEvent(ev, s, emit) {
641
663
  if (plan)
642
664
  emit({ type: 'plan', plan });
643
665
  }
666
+ if (tool)
667
+ emit({ type: 'tool', call: { id, name: tool.name, summary: tool.summary, status: b.is_error ? 'failed' : 'done', result: null } });
644
668
  continue;
645
669
  }
646
670
  if (!tool)
647
671
  continue;
648
672
  const isError = !!b.is_error;
649
- // File-shaped tools have no useful result detail (mirrors pikiloom): just mark done.
650
- const fileTool = tool.name === 'Read' || tool.name === 'Edit' || tool.name === 'Write' || tool.name === 'TodoWrite';
673
+ // File-shaped and task-list tools have no useful result detail (mirrors pikiloom): just mark done.
674
+ const fileTool = tool.name === 'Read' || tool.name === 'Edit' || tool.name === 'Write'
675
+ || tool.name === 'TodoWrite' || tool.name === 'TaskCreate' || tool.name === 'TaskUpdate';
651
676
  const detail = (isError || !fileTool) ? firstResultLine(b.content) : null;
652
677
  emit({ type: 'tool', call: { id, name: tool.name, summary: tool.summary, status: isError ? 'failed' : 'done', result: detail || null } });
653
678
  }
@@ -960,6 +985,30 @@ export function rebuildClaudeTaskPlan(s) {
960
985
  }
961
986
  return steps.length ? { explanation: null, steps } : null;
962
987
  }
988
+ // Apply a TaskUpdate to a TodoWrite-produced plan positionally (taskId = 1-based item index).
989
+ // Used when the id isn't in the TaskCreate store, so an update issued AFTER a TodoWrite still
990
+ // lands on the displayed list — the plan reflects the state after the LAST change, whichever
991
+ // mechanism wrote it. Returns a fresh plan (never mutates) or null when inapplicable.
992
+ export function applyTaskUpdateToTodoPlan(plan, taskId, rawStatus) {
993
+ if (!plan || !Array.isArray(plan.steps) || !plan.steps.length)
994
+ return null;
995
+ if (!/^\d+$/.test(taskId))
996
+ return null;
997
+ const idx = Number(taskId) - 1;
998
+ if (idx < 0 || idx >= plan.steps.length)
999
+ return null;
1000
+ if (rawStatus === 'deleted') {
1001
+ const steps = plan.steps.filter((_, i) => i !== idx);
1002
+ return steps.length ? { explanation: plan.explanation ?? null, steps } : null;
1003
+ }
1004
+ const status = rawStatus === 'completed' ? 'completed'
1005
+ : (rawStatus === 'in_progress' || rawStatus === 'inprogress') ? 'inProgress'
1006
+ : rawStatus === 'pending' ? 'pending' : null;
1007
+ if (!status)
1008
+ return null;
1009
+ const steps = plan.steps.map((step, i) => i === idx ? { ...step, status } : step);
1010
+ return { explanation: plan.explanation ?? null, steps };
1011
+ }
963
1012
  // ── Tool-call summarization (ported from pikiloom's summarizeClaudeToolUse) ──────────
964
1013
  // Turns a Claude tool_use {name,input} into a one-line human summary. The runtime's
965
1014
  // activity projector joins these into snapshot.activity; the structured form lives in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikiloom/kernel",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "description": "Heterogeneous coding agents (Claude / Codex / Gemini / ACP) -> an interaction-friendly, accumulating session snapshot + control handle, over pluggable surfaces (IM, Web, tunnel, TUI). The reusable core pikiloom itself is built on.",
5
5
  "type": "module",
6
6
  "license": "MIT",