@dorokuma/herdsman-pi 0.6.0 → 0.6.1

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/package.json +3 -2
  2. package/src/index.ts +47 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorokuma/herdsman-pi",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Pi extension bridge for Herdsman agent history. Forked from @ryonakae/shepherd.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -10,7 +10,8 @@
10
10
  ],
11
11
  "license": "MIT",
12
12
  "files": [
13
- "src"
13
+ "src",
14
+ "../LICENSE"
14
15
  ],
15
16
  "publishConfig": {
16
17
  "access": "public"
package/src/index.ts CHANGED
@@ -16,7 +16,6 @@ import {
16
16
  } from "./agent-update-ui.js";
17
17
  import {
18
18
  projectAgentOutcomes,
19
- createAgentOutcomeProjector,
20
19
  formatAgentOutcomeUpdates,
21
20
  WAKE_SETTLE_MS,
22
21
  } from "./wake.js";
@@ -76,6 +75,7 @@ type LaunchIdentity = {
76
75
  type DeliveredBatch = {
77
76
  assistantFinalSucceeded: boolean;
78
77
  events: AgentEventWireRecord[];
78
+ hasSubstantiveWork: boolean;
79
79
  invalidated: boolean;
80
80
  ownerTerminalId: string;
81
81
  herdsmanTriggered: boolean;
@@ -162,8 +162,6 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
162
162
  return function herdsmanPiExtension(pi: PiApi): void {
163
163
  pi.registerMessageRenderer?.("herdsman-wake", renderAgentUpdateMessage);
164
164
 
165
- const projector = createAgentOutcomeProjector();
166
-
167
165
  const state: HerdsmanState = {
168
166
  client: undefined,
169
167
  connected: false,
@@ -301,18 +299,21 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
301
299
  return;
302
300
  }
303
301
 
304
- const current = projectAgentOutcomes(state.pendingEvents).outcomes.filter(
302
+ const batchEvents = [...state.pendingEvents].sort((left, right) => left.id - right.id);
303
+ const batchOutcomes = projectAgentOutcomes(batchEvents).outcomes.filter(
305
304
  (outcome) => outcome.eventId > state.failedWakeThroughEventId,
306
305
  );
307
- if (current.length === 0) {
306
+ if (batchOutcomes.length === 0) {
308
307
  state.wakeTimer = undefined;
309
308
  return;
310
309
  }
311
- const batchEvents = [...state.pendingEvents].sort((left, right) => left.id - right.id);
312
- const batchOutcomes = projector(batchEvents).outcomes;
310
+ const current = batchOutcomes;
313
311
  state.deliveredBatch = {
314
312
  assistantFinalSucceeded: false,
315
- events: batchEvents,
313
+ events: batchEvents.filter((event) =>
314
+ batchOutcomes.some((outcome) => outcome.eventId === event.id),
315
+ ),
316
+ hasSubstantiveWork: false,
316
317
  invalidated: false,
317
318
  ownerTerminalId,
318
319
  herdsmanTriggered: true,
@@ -348,7 +349,7 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
348
349
  }, WAKE_SETTLE_MS);
349
350
  };
350
351
 
351
- const loseRole = (ctx: PiContext | undefined) => {
352
+ const loseRole = (ctx: PiContext | undefined, options: { abort?: boolean } = {}) => {
352
353
  if (state.deliveredBatch) {
353
354
  state.deliveredBatch.invalidated = true;
354
355
  const lastEventId = state.deliveredBatch.events.at(-1)?.id;
@@ -358,7 +359,13 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
358
359
  lastEventId,
359
360
  );
360
361
  }
361
- if (state.deliveredBatch.herdsmanTriggered) ctx?.abort?.();
362
+ if (
363
+ options.abort &&
364
+ state.deliveredBatch.herdsmanTriggered &&
365
+ !state.deliveredBatch.hasSubstantiveWork
366
+ ) {
367
+ ctx?.abort?.();
368
+ }
362
369
  }
363
370
  if (state.wakeRequestedThroughEventId > 0) {
364
371
  state.failedWakeThroughEventId = Math.max(
@@ -376,14 +383,21 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
376
383
 
377
384
  const markDisconnected = (ctx: PiContext | undefined) => {
378
385
  const reconnectingFromOn = state.reconnectingFromOn || state.isOrchestrator;
379
- loseRole(ctx);
386
+ loseRole(ctx, { abort: false });
380
387
  state.reconnectingFromOn = reconnectingFromOn;
381
388
  setHerdsmanUi(ctx);
382
389
  };
383
390
 
384
391
  const resetForScopeChange = (ctx: PiContext | undefined) => {
385
392
  clearAgentContext();
386
- if (state.deliveredBatch?.herdsmanTriggered) ctx?.abort?.();
393
+ if (
394
+ state.deliveredBatch?.herdsmanTriggered &&
395
+ !state.deliveredBatch.hasSubstantiveWork
396
+ ) {
397
+ // Scope changes invalidate the batch; an in-flight wake is only aborted
398
+ // when it is still the pure, empty Herdsman wake turn.
399
+ ctx?.abort?.();
400
+ }
387
401
  if (state.deliveredBatch) state.deliveredBatch.invalidated = true;
388
402
  state.deliveredBatch = undefined;
389
403
  cancelWake();
@@ -488,7 +502,7 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
488
502
  terminalId,
489
503
  workspaceId: change.current.workspaceId,
490
504
  };
491
- loseRole(ctx);
505
+ loseRole(ctx, { abort: true });
492
506
  if (!state.roleMutationInFlight) {
493
507
  ctx?.ui.notify?.(
494
508
  change.current.owner
@@ -643,9 +657,24 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
643
657
  if (state.deliveredBatch) {
644
658
  state.deliveredBatch.assistantFinalSucceeded =
645
659
  stopReason === "stop" || stopReason === "length";
660
+ if (
661
+ message.content !== undefined &&
662
+ ((typeof message.content === "string" && message.content.length > 0) ||
663
+ (Array.isArray(message.content) && message.content.length > 0))
664
+ ) {
665
+ state.deliveredBatch.hasSubstantiveWork = true;
666
+ }
646
667
  }
647
668
  });
648
669
 
670
+ pi.on("tool_execution_start", () => {
671
+ if (state.deliveredBatch) state.deliveredBatch.hasSubstantiveWork = true;
672
+ });
673
+
674
+ pi.on("tool_result", () => {
675
+ if (state.deliveredBatch) state.deliveredBatch.hasSubstantiveWork = true;
676
+ });
677
+
649
678
  pi.on("agent_start", () => {
650
679
  if (state.runActive) return;
651
680
  state.runActive = true;
@@ -731,6 +760,11 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
731
760
  "Herdsman couldn’t acknowledge agent updates · updates remain pending",
732
761
  "warning",
733
762
  );
763
+ const failedEventId = event.id;
764
+ state.failedWakeThroughEventId = Math.max(
765
+ state.failedWakeThroughEventId,
766
+ failedEventId,
767
+ );
734
768
  break;
735
769
  }
736
770
  }