@anhlp/alp-runtime 0.0.0-beta.2 → 0.0.1-beta.3

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.
@@ -114,6 +114,7 @@ export declare class ClaudeTransport {
114
114
  private activeTurn?;
115
115
  private closed;
116
116
  private assistantText;
117
+ private streamingMessage?;
117
118
  private toolCalls;
118
119
  /** The last rate limit Claude reported, and whether the running turn hit one. */
119
120
  private rateLimit?;
@@ -165,6 +166,8 @@ export declare class ClaudeTransport {
165
166
  private consume;
166
167
  private handle;
167
168
  private emit;
169
+ private assistantState;
170
+ private clearAssistantText;
168
171
  private fail;
169
172
  close(): Promise<void>;
170
173
  }
@@ -51,12 +51,14 @@ var toolShapes = {
51
51
  body: z.string().min(1).max(2e4),
52
52
  roles: z.array(z.string().min(1)).min(1).max(10),
53
53
  lessons: z.array(z.string().min(1).max(600)).max(50).optional(),
54
- replace: z.boolean().optional()
54
+ replace: z.boolean().optional(),
55
+ requestId: z.string().min(1).max(100).optional()
55
56
  },
56
57
  alp_issue: {
57
58
  action: z.enum(["search", "create", "comment"]),
58
59
  target: z.enum(["project", "alp"]),
59
60
  query: z.string().min(1).max(200).optional(),
61
+ requestId: z.string().min(1).max(100).optional(),
60
62
  title: z.string().min(1).max(200).optional(),
61
63
  body: z.string().min(1).max(2e4).optional(),
62
64
  issue: z.number().int().positive().optional(),
@@ -285,6 +287,7 @@ var ClaudeTransport = class {
285
287
  activeTurn;
286
288
  closed = false;
287
289
  assistantText = /* @__PURE__ */ new Map();
290
+ streamingMessage;
288
291
  toolCalls = /* @__PURE__ */ new Map();
289
292
  /** The last rate limit Claude reported, and whether the running turn hit one. */
290
293
  rateLimit;
@@ -353,6 +356,7 @@ var ClaudeTransport = class {
353
356
  return {};
354
357
  }
355
358
  if (method === "thread/start" || method === "thread/resume" || method === "thread/fork") {
359
+ this.clearAssistantText();
356
360
  this.config = params;
357
361
  this.threadId = method === "thread/resume" ? params.threadId : randomUUID();
358
362
  await this.start(method === "thread/start" ? void 0 : { resume: params.threadId, fork: method === "thread/fork" });
@@ -366,7 +370,10 @@ var ClaudeTransport = class {
366
370
  if (method === "turn/start" || method === "turn/steer") {
367
371
  if (!this.query) throw new Error("Claude thread is not initialized");
368
372
  const turnId = method === "turn/steer" ? params.expectedTurnId : randomUUID();
369
- if (method === "turn/start") this.activeTurn = turnId;
373
+ if (method === "turn/start") {
374
+ this.clearAssistantText();
375
+ this.activeTurn = turnId;
376
+ }
370
377
  this.input.push({
371
378
  type: "user",
372
379
  message: {
@@ -380,6 +387,7 @@ var ClaudeTransport = class {
380
387
  return method === "turn/start" ? { turn: { id: turnId } } : {};
381
388
  }
382
389
  if (method === "turn/interrupt") {
390
+ this.streamingMessage = void 0;
383
391
  await this.query?.interrupt();
384
392
  return {};
385
393
  }
@@ -483,7 +491,7 @@ var ClaudeTransport = class {
483
491
  ...plugins.length ? { plugins } : {},
484
492
  persistSession: !config.ephemeral,
485
493
  promptSuggestions: false,
486
- includePartialMessages: false,
494
+ includePartialMessages: true,
487
495
  ...from ? { resume: from.resume, ...from.fork ? { forkSession: true, sessionId: this.threadId } : {} } : { sessionId: this.threadId },
488
496
  // Explicit native path avoids SDK optional binaries inside Electron app.asar.
489
497
  pathToClaudeCodeExecutable: this.command
@@ -521,15 +529,42 @@ var ClaudeTransport = class {
521
529
  if (!this.closed) this.fail(new Error("Claude session exited"));
522
530
  }
523
531
  handle(message) {
532
+ if (this.closed) return;
524
533
  if (message.type === "rate_limit_event") {
525
534
  this.rateLimit = message.rate_limit_info;
526
535
  this.emit("account/rateLimits/updated", { claude: message.rate_limit_info });
527
536
  return;
528
537
  }
529
- if (message.type === "assistant" && !message.parent_tool_use_id && !this.activeTurn && !this.closed) {
538
+ const streamStart = message.type === "stream_event" && message.event?.type === "message_start";
539
+ if ((message.type === "assistant" || streamStart) && !message.parent_tool_use_id && !this.activeTurn) {
530
540
  this.activeTurn = randomUUID();
531
541
  this.emit("turn/started", { threadId: this.threadId, turn: { id: this.activeTurn } });
532
542
  }
543
+ if (message.type === "stream_event") {
544
+ if (message.parent_tool_use_id) return;
545
+ const event = message.event;
546
+ if (event.type === "message_start") {
547
+ this.streamingMessage = event.message.id;
548
+ } else if (event.type === "message_stop") {
549
+ this.streamingMessage = void 0;
550
+ } else if (this.streamingMessage) {
551
+ const id = this.streamingMessage;
552
+ const state = this.assistantState(id);
553
+ let delta;
554
+ if (event.type === "content_block_start" && event.content_block.type === "text") {
555
+ state.blocks.set(event.index, { text: event.content_block.text, final: false });
556
+ delta = event.content_block.text;
557
+ } else if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
558
+ const block = state.blocks.get(event.index);
559
+ if (block && !block.final) {
560
+ block.text += event.delta.text;
561
+ delta = event.delta.text;
562
+ }
563
+ }
564
+ if (delta) this.emit("item/agentMessage/delta", { threadId: this.threadId, turnId: this.activeTurn, itemId: id, delta });
565
+ }
566
+ return;
567
+ }
533
568
  if (message.type === "assistant" && message.error === "rate_limit") this.limitedTurn = true;
534
569
  if (message.type === "system" && (message.subtype === "status" || message.subtype === "compact_boundary")) {
535
570
  const system = message;
@@ -567,14 +602,18 @@ var ClaudeTransport = class {
567
602
  if (windows.length && !this.compactAt) this.contextWindow = Math.max(...windows);
568
603
  }
569
604
  if (message.type === "assistant" && !message.parent_tool_use_id) {
605
+ const id = message.message.id;
606
+ const state = this.assistantState(id);
607
+ if (message.uuid && state.seen.has(message.uuid)) return;
608
+ if (message.uuid) state.seen.add(message.uuid);
570
609
  for (const block of message.message.content) {
571
610
  if (block.type === "text") {
572
- const id = message.message.id;
573
- const text = (this.assistantText.get(id) ?? "") + block.text;
574
- this.assistantText.set(id, text);
611
+ const pending = [...state.blocks.entries()].sort(([a], [b]) => a - b).find(([, value]) => !value.final);
612
+ const index = pending?.[0] ?? Math.max(-1, ...state.blocks.keys()) + 1;
613
+ state.blocks.set(index, { text: block.text, final: true });
575
614
  this.emit("item/completed", {
576
615
  threadId: this.threadId,
577
- item: { type: "agentMessage", id, text }
616
+ item: { type: "agentMessage", id, text: [...state.blocks.entries()].sort(([a], [b]) => a - b).map(([, block2]) => block2.text).join("") }
578
617
  });
579
618
  } else if (block.type === "tool_use") {
580
619
  const command = block.name === "Bash" ? String(block.input?.command ?? "") : `${block.name} ${JSON.stringify(block.input ?? {})}`;
@@ -623,6 +662,7 @@ var ClaudeTransport = class {
623
662
  }
624
663
  return;
625
664
  }
665
+ if (message.type === "result") this.clearAssistantText();
626
666
  if (message.type === "result" && this.activeTurn) {
627
667
  const turnId = this.activeTurn;
628
668
  this.activeTurn = void 0;
@@ -645,14 +685,25 @@ var ClaudeTransport = class {
645
685
  emit(method, params) {
646
686
  for (const listener of this.listeners) listener(method, params);
647
687
  }
688
+ assistantState(id) {
689
+ let state = this.assistantText.get(id);
690
+ if (!state) this.assistantText.set(id, state = { blocks: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new Set() });
691
+ return state;
692
+ }
693
+ clearAssistantText() {
694
+ this.streamingMessage = void 0;
695
+ this.assistantText.clear();
696
+ }
648
697
  fail(value) {
649
698
  if (this.closed) return;
699
+ this.clearAssistantText();
650
700
  const error = value instanceof Error ? value : new Error(String(value));
651
701
  for (const listener of this.failures) listener(error);
652
702
  }
653
703
  async close() {
654
704
  if (this.closed) return;
655
705
  this.closed = true;
706
+ this.clearAssistantText();
656
707
  this.input.close();
657
708
  this.query?.close();
658
709
  await this.pump?.catch(() => {
@@ -146,9 +146,10 @@ export type AlpRuntime = {
146
146
  }, question: string): Promise<RecallAnswer>;
147
147
  /** Assignments an earlier alpd left running, which recover() can continue once their root is open again. */
148
148
  recoverable(): LiveEntry[];
149
- /** Continues the assignments an earlier alpd left running in an open root's tree; with continueRoot, the root's own turn too. */
149
+ /** Continues work in a reopened tree. continueRoot resumes its running turn; parkedRoot restores its held continuation intent. */
150
150
  recover(rootId: string, options?: {
151
151
  continueRoot?: boolean;
152
+ parkedRoot?: string;
152
153
  }): Promise<RecoveryOutcome[]>;
153
154
  /** Gives up the assignments of a tree whose root cannot be opened again: their tasks go back to open. */
154
155
  abandon(rootId: string, reason: string): Promise<RecoveryOutcome[]>;
@@ -37,7 +37,7 @@ export declare function claudeUsage(result: any): {
37
37
  };
38
38
  /**
39
39
  * Whether a runtime's usage report (codexUsage or claudeUsage) lets it work again (ALPD §58):
40
- * true when every window has room, false when one is used up or spending is capped, and
40
+ * true when every window has room, false on explicit provider denial, a used-up window or spending cap, and
41
41
  * undefined when the report says nothing either way.
42
42
  */
43
43
  export declare function usageAllows(usage: any): boolean | undefined;
@@ -46,7 +46,7 @@ function claudeUsage(result) {
46
46
  function usageAllows(usage) {
47
47
  if (!usage?.available) return void 0;
48
48
  const limits = Array.isArray(usage.limits) ? usage.limits : [];
49
- if (limits.some((limit) => limit?.spendControlReached === true)) return false;
49
+ if (usage.ordinaryUsageAllowed === false || limits.some((limit) => limit?.spendControlReached === true)) return false;
50
50
  const windows = limits.length ? limits.flatMap((limit) => limit?.windows ?? []) : usage.windows ?? [];
51
51
  const known = windows.filter((window) => typeof window?.usedPercent === "number");
52
52
  if (!known.length) return void 0;