happy-imou-cloud 2.0.20 → 2.0.21

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 (28) hide show
  1. package/dist/{BaseReasoningProcessor-B9z785Pi.cjs → BaseReasoningProcessor-0nj-PMFc.cjs} +3 -3
  2. package/dist/{BaseReasoningProcessor-iTk5q5Vy.mjs → BaseReasoningProcessor-DnVC7liC.mjs} +3 -3
  3. package/dist/{ProviderSelectionHandler-DUBEXkmo.cjs → ProviderSelectionHandler-Bafuy28L.cjs} +2 -2
  4. package/dist/{ProviderSelectionHandler-s79sTquD.mjs → ProviderSelectionHandler-R-2r7ItM.mjs} +2 -2
  5. package/dist/{api-_Y2kzqZL.mjs → api-DJe9WP9M.mjs} +77 -4
  6. package/dist/{api-BKnzORe4.cjs → api-MGlKcEf3.cjs} +78 -3
  7. package/dist/{command-BDkgHdQU.mjs → command-CfyFnMv2.mjs} +3 -3
  8. package/dist/{command-BBNzMWEG.cjs → command-DAlFmWmr.cjs} +3 -3
  9. package/dist/{index-Bg4KLXYY.mjs → index-CHXCgpwi.mjs} +8 -8
  10. package/dist/{index-0TIyXMQu.cjs → index-CgVjDJpt.cjs} +11 -11
  11. package/dist/index.cjs +3 -3
  12. package/dist/index.mjs +3 -3
  13. package/dist/lib.cjs +1 -1
  14. package/dist/lib.d.cts +393 -36
  15. package/dist/lib.d.mts +393 -36
  16. package/dist/lib.mjs +1 -1
  17. package/dist/{persistence-BCkHc68b.mjs → persistence-CkP90vEt.mjs} +1 -1
  18. package/dist/{persistence-D5uolhHo.cjs → persistence-DLFUNI9q.cjs} +1 -1
  19. package/dist/{registerKillSessionHandler-BMUE5Oaj.cjs → registerKillSessionHandler-Cs_INk4A.cjs} +356 -3
  20. package/dist/{registerKillSessionHandler-DJMH-gar.mjs → registerKillSessionHandler-DsHTZDsU.mjs} +354 -4
  21. package/dist/{runClaude-BUhD2jR2.cjs → runClaude-BGSgcyUp.cjs} +72 -9
  22. package/dist/{runClaude-BMv-eao6.mjs → runClaude-DAQAEmHe.mjs} +72 -9
  23. package/dist/{runCodex-DwnLnXWK.mjs → runCodex-B2UpSn82.mjs} +84 -14
  24. package/dist/{runCodex-DyNSDN6X.cjs → runCodex-earICaxw.cjs} +84 -14
  25. package/dist/{runGemini-uVHDcJ09.mjs → runGemini-BBUmH1Qh.mjs} +73 -9
  26. package/dist/{runGemini-UKpRbyNY.cjs → runGemini-D5RAIaR0.cjs} +73 -9
  27. package/package.json +1 -1
  28. package/scripts/release-smoke.mjs +3 -3
@@ -1,8 +1,8 @@
1
- import { i as initialMachineMetadata, e as projectPath, R as RuntimeShell, h as resolveCanonicalToolNameV2, f as formatDisplayMessage } from './index-Bg4KLXYY.mjs';
2
- import { readSettings } from './persistence-BCkHc68b.mjs';
1
+ import { i as initialMachineMetadata, e as projectPath, R as RuntimeShell, h as resolveCanonicalToolNameV2, f as formatDisplayMessage } from './index-CHXCgpwi.mjs';
2
+ import { readSettings } from './persistence-CkP90vEt.mjs';
3
3
  import os from 'node:os';
4
4
  import { resolve } from 'node:path';
5
- import { c as configuration, p as packageJson, l as logger } from './api-_Y2kzqZL.mjs';
5
+ import { c as configuration, p as packageJson, H as HAPPY_ORG_TURN_REPORT_TAG, d as HAPPY_ORG_REPEAT_THRESHOLD, l as logger } from './api-DJe9WP9M.mjs';
6
6
  import { randomUUID } from 'node:crypto';
7
7
  import { createHash } from 'crypto';
8
8
  import 'axios';
@@ -351,6 +351,356 @@ async function syncControlledByUserState(sessionClient, controlledByUser) {
351
351
  });
352
352
  }
353
353
 
354
+ function normalizeOptionalText(value) {
355
+ if (typeof value !== "string") {
356
+ return null;
357
+ }
358
+ const trimmed = value.trim();
359
+ return trimmed.length > 0 ? trimmed : null;
360
+ }
361
+ function cloneHappyOrgMetadata(happyOrg) {
362
+ return {
363
+ taskContext: happyOrg?.taskContext ? { ...happyOrg.taskContext } : void 0,
364
+ runtime: happyOrg?.runtime ? { ...happyOrg.runtime } : void 0,
365
+ activeOwner: happyOrg?.activeOwner ? { ...happyOrg.activeOwner } : null,
366
+ repeat: happyOrg?.repeat ? {
367
+ threshold: happyOrg.repeat.threshold,
368
+ fingerprints: Object.fromEntries(
369
+ Object.entries(happyOrg.repeat.fingerprints ?? {}).map(([fingerprint, entry]) => [
370
+ fingerprint,
371
+ { ...entry }
372
+ ])
373
+ )
374
+ } : void 0,
375
+ lastTurnReport: happyOrg?.lastTurnReport ? { ...happyOrg.lastTurnReport } : void 0
376
+ };
377
+ }
378
+ function normalizeHappyOrgMetadata(metadata) {
379
+ const happyOrg = cloneHappyOrgMetadata(metadata?.happyOrg);
380
+ return {
381
+ ...happyOrg,
382
+ runtime: happyOrg.runtime ?? {
383
+ status: "active",
384
+ reason: null
385
+ },
386
+ repeat: happyOrg.repeat ?? {
387
+ threshold: HAPPY_ORG_REPEAT_THRESHOLD,
388
+ fingerprints: {}
389
+ }
390
+ };
391
+ }
392
+ function withHappyOrgMetadata(metadata, happyOrg) {
393
+ return {
394
+ ...metadata,
395
+ happyOrg
396
+ };
397
+ }
398
+ function resetHappyOrgRuntimeForTask(taskContext) {
399
+ return {
400
+ taskContext,
401
+ runtime: {
402
+ status: "active",
403
+ reason: null
404
+ },
405
+ activeOwner: null,
406
+ repeat: {
407
+ threshold: HAPPY_ORG_REPEAT_THRESHOLD,
408
+ fingerprints: {}
409
+ }
410
+ };
411
+ }
412
+ function buildTerminatedStatusMessage(taskId) {
413
+ return `Task ${taskId} is terminated. Reopen it with new context, a new decision, or a new resource before continuing.`;
414
+ }
415
+ function buildOwnerConflictStatusMessage(taskId, ownerAgentId) {
416
+ return `Task ${taskId} is already active under owner ${ownerAgentId}. This turn must exit without continuing token usage.`;
417
+ }
418
+ function inferInterventionType(draft) {
419
+ if (draft?.interventionType === "none" || draft?.interventionType === "review_needed" || draft?.interventionType === "blocker" || draft?.interventionType === "decision_needed") {
420
+ return draft.interventionType;
421
+ }
422
+ if (normalizeOptionalText(draft?.decisionNeeded)) {
423
+ return "decision_needed";
424
+ }
425
+ if (normalizeOptionalText(draft?.blockerCode)) {
426
+ return "blocker";
427
+ }
428
+ return "none";
429
+ }
430
+ function buildFallbackSummary(text, turnStatus) {
431
+ const trimmed = text.trim();
432
+ if (trimmed) {
433
+ return trimmed.replace(/\s+/g, " ").slice(0, 160);
434
+ }
435
+ return turnStatus === "turn_aborted" ? "Turn aborted before completion." : "Turn completed without a textual summary.";
436
+ }
437
+ function normalizeTurnReportDraft(draft) {
438
+ return {
439
+ summary: normalizeOptionalText(draft?.summary)?.replace(/\s+/g, " ").slice(0, 160) ?? null,
440
+ interventionType: inferInterventionType(draft),
441
+ blockerCode: normalizeOptionalText(draft?.blockerCode),
442
+ decisionNeeded: normalizeOptionalText(draft?.decisionNeeded),
443
+ targetArtifact: normalizeOptionalText(draft?.targetArtifact)
444
+ };
445
+ }
446
+ function stripCodeFence(text) {
447
+ return text.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim();
448
+ }
449
+ function extractTaggedTurnReport(text) {
450
+ const matcher = new RegExp(
451
+ `<${HAPPY_ORG_TURN_REPORT_TAG}>\\s*([\\s\\S]*?)\\s*</${HAPPY_ORG_TURN_REPORT_TAG}>`,
452
+ "gi"
453
+ );
454
+ let lastMatch = null;
455
+ for (let match = matcher.exec(text); match; match = matcher.exec(text)) {
456
+ lastMatch = match;
457
+ }
458
+ if (!lastMatch) {
459
+ return {
460
+ cleanedText: text.trim(),
461
+ draft: null
462
+ };
463
+ }
464
+ const rawBlock = stripCodeFence(lastMatch[1] ?? "");
465
+ let draft = null;
466
+ try {
467
+ const parsed = JSON.parse(rawBlock);
468
+ draft = {
469
+ summary: normalizeOptionalText(parsed.summary),
470
+ interventionType: normalizeOptionalText(parsed.interventionType),
471
+ blockerCode: normalizeOptionalText(parsed.blockerCode),
472
+ decisionNeeded: normalizeOptionalText(parsed.decisionNeeded),
473
+ targetArtifact: normalizeOptionalText(parsed.targetArtifact)
474
+ };
475
+ } catch {
476
+ draft = null;
477
+ }
478
+ const cleanedText = `${text.slice(0, lastMatch.index)}${text.slice(lastMatch.index + lastMatch[0].length)}`.replace(/\n{3,}/g, "\n\n").trim();
479
+ return {
480
+ cleanedText,
481
+ draft
482
+ };
483
+ }
484
+ function buildRepeatFingerprint(context, blockerCode, targetArtifact) {
485
+ if (!blockerCode) {
486
+ return null;
487
+ }
488
+ return [
489
+ context.taskId,
490
+ context.memberAgentId,
491
+ blockerCode,
492
+ targetArtifact ?? ""
493
+ ].join("::");
494
+ }
495
+ function buildHappyOrgTurnPrompt(prompt, turn) {
496
+ const reopenLines = turn.reopenContext ? [
497
+ "",
498
+ "This task was explicitly reopened for this turn with the following new inputs:",
499
+ turn.reopenContext.newContext ? `- newContext: ${turn.reopenContext.newContext}` : null,
500
+ turn.reopenContext.newDecision ? `- newDecision: ${turn.reopenContext.newDecision}` : null,
501
+ turn.reopenContext.newResource ? `- newResource: ${turn.reopenContext.newResource}` : null
502
+ ].filter(Boolean) : [];
503
+ const header = [
504
+ "[HAPPY_ORG_TASK_CONTEXT]",
505
+ `taskId=${turn.context.taskId}`,
506
+ `organizationId=${turn.context.organizationId}`,
507
+ `memberAgentId=${turn.context.memberAgentId}`,
508
+ `supervisorAgentId=${turn.context.supervisorAgentId}`,
509
+ "Stay on this exact task for the whole turn.",
510
+ "End your response with exactly one raw JSON block inside these tags and do not wrap it in a markdown code fence:",
511
+ `<${HAPPY_ORG_TURN_REPORT_TAG}>{"summary":"short factual summary","interventionType":"none","blockerCode":null,"decisionNeeded":null,"targetArtifact":null}</${HAPPY_ORG_TURN_REPORT_TAG}>`,
512
+ "Allowed interventionType values: none, review_needed, blocker, decision_needed.",
513
+ "summary must read like a CEO todo card title: short, actionable, and without long process logs.",
514
+ "Use blocker only for problems the organization may still solve internally.",
515
+ "Use decision_needed only when a CEO or user decision is required.",
516
+ "Use review_needed when supervisor review is needed but the work is not blocked.",
517
+ ...reopenLines,
518
+ "[/HAPPY_ORG_TASK_CONTEXT]",
519
+ "",
520
+ prompt
521
+ ];
522
+ return header.join("\n");
523
+ }
524
+ function resolveHappyOrgQueuedTurn(opts) {
525
+ const metadata = opts.metadata ?? null;
526
+ if (!metadata) {
527
+ return {
528
+ nextMetadata: null,
529
+ queuedTurn: null,
530
+ blocked: false
531
+ };
532
+ }
533
+ const currentHappyOrg = normalizeHappyOrgMetadata(metadata);
534
+ let nextHappyOrg = cloneHappyOrgMetadata(currentHappyOrg);
535
+ const messageHappyOrg = opts.message.meta?.happyOrg;
536
+ const now = opts.now?.() ?? Date.now();
537
+ const createRunId = opts.createRunId ?? ((taskContext2, currentNow) => `${taskContext2.taskId}:${taskContext2.memberAgentId}:${currentNow}`);
538
+ if (messageHappyOrg?.taskContext) {
539
+ const currentTaskId = currentHappyOrg.taskContext?.taskId ?? null;
540
+ if (currentTaskId !== messageHappyOrg.taskContext.taskId) {
541
+ nextHappyOrg = resetHappyOrgRuntimeForTask(messageHappyOrg.taskContext);
542
+ } else {
543
+ nextHappyOrg.taskContext = { ...messageHappyOrg.taskContext };
544
+ }
545
+ }
546
+ const taskContext = nextHappyOrg.taskContext ?? null;
547
+ if (!taskContext) {
548
+ return {
549
+ nextMetadata: metadata,
550
+ queuedTurn: null,
551
+ blocked: false
552
+ };
553
+ }
554
+ const control = messageHappyOrg?.control;
555
+ if (control?.action === "terminate") {
556
+ nextHappyOrg.runtime = {
557
+ status: "terminated",
558
+ reason: normalizeOptionalText(control.reason) ?? "terminated_by_supervisor",
559
+ terminatedAt: now
560
+ };
561
+ nextHappyOrg.activeOwner = null;
562
+ return {
563
+ nextMetadata: withHappyOrgMetadata(metadata, nextHappyOrg),
564
+ queuedTurn: null,
565
+ blocked: true,
566
+ statusMessage: buildTerminatedStatusMessage(taskContext.taskId)
567
+ };
568
+ }
569
+ const hasReopenInputs = Boolean(
570
+ normalizeOptionalText(control?.newContext) || normalizeOptionalText(control?.newDecision) || normalizeOptionalText(control?.newResource)
571
+ );
572
+ if (nextHappyOrg.runtime?.status === "terminated") {
573
+ if (control?.action !== "reopen") {
574
+ return {
575
+ nextMetadata: withHappyOrgMetadata(metadata, nextHappyOrg),
576
+ queuedTurn: null,
577
+ blocked: true,
578
+ statusMessage: buildTerminatedStatusMessage(taskContext.taskId)
579
+ };
580
+ }
581
+ if (!hasReopenInputs) {
582
+ return {
583
+ nextMetadata: withHappyOrgMetadata(metadata, nextHappyOrg),
584
+ queuedTurn: null,
585
+ blocked: true,
586
+ statusMessage: `Task ${taskContext.taskId} can only reopen with new context, a new decision, or a new resource.`
587
+ };
588
+ }
589
+ }
590
+ const reopenContext = control?.action === "reopen" && hasReopenInputs ? {
591
+ newContext: normalizeOptionalText(control.newContext),
592
+ newDecision: normalizeOptionalText(control.newDecision),
593
+ newResource: normalizeOptionalText(control.newResource)
594
+ } : void 0;
595
+ if (reopenContext) {
596
+ nextHappyOrg.runtime = {
597
+ status: "active",
598
+ reason: null,
599
+ reopenedAt: now
600
+ };
601
+ } else if (!nextHappyOrg.runtime) {
602
+ nextHappyOrg.runtime = {
603
+ status: "active",
604
+ reason: null
605
+ };
606
+ }
607
+ if (!nextHappyOrg.activeOwner) {
608
+ nextHappyOrg.activeOwner = {
609
+ ownerAgentId: taskContext.memberAgentId,
610
+ ownerRunId: createRunId(taskContext, now),
611
+ claimedAt: now
612
+ };
613
+ } else if (nextHappyOrg.activeOwner.ownerAgentId !== taskContext.memberAgentId) {
614
+ return {
615
+ nextMetadata: withHappyOrgMetadata(metadata, nextHappyOrg),
616
+ queuedTurn: null,
617
+ blocked: true,
618
+ statusMessage: buildOwnerConflictStatusMessage(
619
+ taskContext.taskId,
620
+ nextHappyOrg.activeOwner.ownerAgentId
621
+ )
622
+ };
623
+ }
624
+ return {
625
+ nextMetadata: withHappyOrgMetadata(metadata, nextHappyOrg),
626
+ queuedTurn: {
627
+ context: taskContext,
628
+ ...reopenContext ? { reopenContext } : {}
629
+ },
630
+ blocked: false
631
+ };
632
+ }
633
+ function finalizeHappyOrgTurn(opts) {
634
+ const metadata = opts.metadata ?? null;
635
+ const queuedTurn = opts.queuedTurn ?? null;
636
+ const { cleanedText, draft } = extractTaggedTurnReport(opts.responseText);
637
+ if (!metadata || !queuedTurn) {
638
+ return {
639
+ cleanedText,
640
+ report: null,
641
+ nextMetadata: metadata
642
+ };
643
+ }
644
+ const now = opts.now?.() ?? Date.now();
645
+ const normalizedDraft = normalizeTurnReportDraft(draft);
646
+ const report = {
647
+ ...queuedTurn.context,
648
+ turnStatus: opts.turnStatus,
649
+ interventionType: normalizedDraft.interventionType ?? "none",
650
+ summary: normalizedDraft.summary ?? buildFallbackSummary(cleanedText, opts.turnStatus),
651
+ blockerCode: normalizedDraft.blockerCode ?? null,
652
+ decisionNeeded: normalizedDraft.decisionNeeded ?? null,
653
+ targetArtifact: normalizedDraft.targetArtifact ?? null,
654
+ repeatFingerprint: buildRepeatFingerprint(
655
+ queuedTurn.context,
656
+ normalizedDraft.blockerCode ?? null,
657
+ normalizedDraft.targetArtifact ?? null
658
+ )
659
+ };
660
+ const nextHappyOrg = normalizeHappyOrgMetadata(metadata);
661
+ nextHappyOrg.taskContext = { ...queuedTurn.context };
662
+ nextHappyOrg.lastTurnReport = report;
663
+ nextHappyOrg.activeOwner = null;
664
+ nextHappyOrg.repeat = nextHappyOrg.repeat ?? {
665
+ threshold: HAPPY_ORG_REPEAT_THRESHOLD,
666
+ fingerprints: {}
667
+ };
668
+ let terminateMessage;
669
+ if (report.repeatFingerprint) {
670
+ const currentEntry = nextHappyOrg.repeat.fingerprints[report.repeatFingerprint] ?? {
671
+ count: 0};
672
+ const nextCount = currentEntry.count + 1;
673
+ nextHappyOrg.repeat.fingerprints[report.repeatFingerprint] = {
674
+ count: nextCount,
675
+ lastSeenAt: now
676
+ };
677
+ if (nextCount >= nextHappyOrg.repeat.threshold) {
678
+ nextHappyOrg.runtime = {
679
+ status: "terminated",
680
+ reason: `repeat_fingerprint:${report.repeatFingerprint}`,
681
+ terminatedAt: now
682
+ };
683
+ terminateMessage = `Task ${queuedTurn.context.taskId} hit repeat threshold for ${report.repeatFingerprint} and is now terminated until reopen.`;
684
+ } else if (!nextHappyOrg.runtime || nextHappyOrg.runtime.status !== "terminated") {
685
+ nextHappyOrg.runtime = {
686
+ status: "active",
687
+ reason: null
688
+ };
689
+ }
690
+ } else if (!nextHappyOrg.runtime || nextHappyOrg.runtime.status !== "terminated") {
691
+ nextHappyOrg.runtime = {
692
+ status: "active",
693
+ reason: null
694
+ };
695
+ }
696
+ return {
697
+ cleanedText,
698
+ report,
699
+ nextMetadata: withHappyOrgMetadata(metadata, nextHappyOrg),
700
+ terminateMessage
701
+ };
702
+ }
703
+
354
704
  const DEFAULT_MAX_MESSAGES = 200;
355
705
  const DEFAULT_MAX_CHARACTERS = 1e5;
356
706
  const DEFAULT_MAX_MESSAGE_CHARACTERS = 8e3;
@@ -1176,4 +1526,4 @@ function registerKillSessionHandler(rpcHandlerManager, killThisHappy) {
1176
1526
  });
1177
1527
  }
1178
1528
 
1179
- export { BasePermissionHandler as B, ConversationHistory$1 as C, INTERACTION_SUPERSEDED_ERROR as I, MissingMachineIdError as M, INTERACTION_TIMED_OUT_ERROR as a, MessageQueue2 as b, createSessionMetadata as c, MessageBuffer as d, ensureManagedProviderMachine as e, closeProviderSession as f, getPendingInteractionTimeoutMs as g, hashObject as h, inferToolResultError as i, forwardAgentMessageToProviderSession as j, launchRuntimeHandleWithFactoryResult as l, registerKillSessionHandler as r, syncControlledByUserState as s, waitForResponseCompleteWithAbort as w };
1529
+ export { BasePermissionHandler as B, ConversationHistory$1 as C, INTERACTION_SUPERSEDED_ERROR as I, MissingMachineIdError as M, INTERACTION_TIMED_OUT_ERROR as a, MessageQueue2 as b, createSessionMetadata as c, MessageBuffer as d, ensureManagedProviderMachine as e, registerKillSessionHandler as f, getPendingInteractionTimeoutMs as g, hashObject as h, buildHappyOrgTurnPrompt as i, finalizeHappyOrgTurn as j, closeProviderSession as k, launchRuntimeHandleWithFactoryResult as l, inferToolResultError as m, forwardAgentMessageToProviderSession as n, resolveHappyOrgQueuedTurn as r, syncControlledByUserState as s, waitForResponseCompleteWithAbort as w };
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  var node_crypto = require('node:crypto');
4
- var api = require('./api-BKnzORe4.cjs');
4
+ var api = require('./api-MGlKcEf3.cjs');
5
5
  require('cross-spawn');
6
6
  require('@agentclientprotocol/sdk');
7
- var index = require('./index-0TIyXMQu.cjs');
7
+ var index = require('./index-CgVjDJpt.cjs');
8
8
  require('ps-list');
9
9
  require('fs');
10
10
  require('path');
@@ -15,7 +15,7 @@ var path = require('node:path');
15
15
  var os = require('node:os');
16
16
  var node_child_process = require('node:child_process');
17
17
  require('node:readline');
18
- require('./persistence-D5uolhHo.cjs');
18
+ require('./persistence-DLFUNI9q.cjs');
19
19
  var promises = require('node:fs/promises');
20
20
  var fs = require('fs/promises');
21
21
  require('crypto');
@@ -26,9 +26,9 @@ require('tweetnacl');
26
26
  require('open');
27
27
  var React = require('react');
28
28
  var ink = require('ink');
29
- var ProviderSelectionHandler = require('./ProviderSelectionHandler-DUBEXkmo.cjs');
29
+ var ProviderSelectionHandler = require('./ProviderSelectionHandler-Bafuy28L.cjs');
30
30
  var types = require('./types-DVk3crez.cjs');
31
- var registerKillSessionHandler = require('./registerKillSessionHandler-BMUE5Oaj.cjs');
31
+ var registerKillSessionHandler = require('./registerKillSessionHandler-Cs_INk4A.cjs');
32
32
  require('socket.io-client');
33
33
  require('expo-server-sdk');
34
34
  var node_util = require('node:util');
@@ -1040,6 +1040,13 @@ async function claudeAcpRemoteLauncher(session) {
1040
1040
  message
1041
1041
  });
1042
1042
  };
1043
+ const emitTurnReport = (report) => {
1044
+ session.client.sendAgentMessage("claude", {
1045
+ type: "turn-report",
1046
+ id: node_crypto.randomUUID(),
1047
+ report
1048
+ });
1049
+ };
1043
1050
  const resetTurnState = () => {
1044
1051
  accumulatedResponse = "";
1045
1052
  isResponseInProgress = false;
@@ -1048,13 +1055,16 @@ async function claudeAcpRemoteLauncher(session) {
1048
1055
  currentThinkingMessageId = null;
1049
1056
  session.onThinkingChange(false);
1050
1057
  };
1051
- const emitFinalAssistantMessage = () => {
1052
- const finalMessage = accumulatedResponse.trim();
1058
+ const emitFinalAssistantMessage = (finalMessageText) => {
1059
+ const finalMessage = (finalMessageText ?? accumulatedResponse).trim();
1053
1060
  if (!finalMessage) {
1054
1061
  accumulatedResponse = "";
1055
1062
  isResponseInProgress = false;
1056
1063
  return;
1057
1064
  }
1065
+ if (currentAssistantMessageId) {
1066
+ messageBuffer.updateMessage(currentAssistantMessageId, finalMessage, { mode: "replace" });
1067
+ }
1058
1068
  conversationHistory.addAssistantMessage(finalMessage);
1059
1069
  session.client.sendAgentMessage("claude", {
1060
1070
  type: "message",
@@ -1366,6 +1376,7 @@ ${systemPrompt}` : systemPrompt,
1366
1376
  permissionHandler.setPermissionMode(message.mode.permissionMode);
1367
1377
  messageBuffer.addMessage(message.message, "user");
1368
1378
  let shouldClearHistoryAfterTurn = false;
1379
+ let turnStatus = "task_complete";
1369
1380
  try {
1370
1381
  const activeRuntimeHandle = runtimeHandle ?? await createRuntimeHandle(message.mode);
1371
1382
  let promptToSend = message.message;
@@ -1376,6 +1387,9 @@ ${systemPrompt}` : systemPrompt,
1376
1387
  promptToSend = historyContext + promptToSend;
1377
1388
  api.logger.debug(`[ClaudeACP] Injected conversation history context (${historyContext.length} chars)`);
1378
1389
  }
1390
+ if (message.mode.happyOrg) {
1391
+ promptToSend = registerKillSessionHandler.buildHappyOrgTurnPrompt(promptToSend, message.mode.happyOrg);
1392
+ }
1379
1393
  if (specialCommand.type === "compact") {
1380
1394
  emitStatusMessage("Compaction started");
1381
1395
  }
@@ -1389,6 +1403,7 @@ ${systemPrompt}` : systemPrompt,
1389
1403
  const isAbortError = error instanceof Error && error.name === "AbortError";
1390
1404
  const isExpectedInterruption = isAbortError || abortController.signal.aborted || shouldExit;
1391
1405
  if (isExpectedInterruption) {
1406
+ turnStatus = "turn_aborted";
1392
1407
  session.client.sendAgentMessage("claude", {
1393
1408
  type: "turn_aborted",
1394
1409
  id: node_crypto.randomUUID()
@@ -1405,7 +1420,23 @@ ${systemPrompt}` : systemPrompt,
1405
1420
  currentModeHash = null;
1406
1421
  }
1407
1422
  } finally {
1408
- emitFinalAssistantMessage();
1423
+ const finalizedTurn = registerKillSessionHandler.finalizeHappyOrgTurn({
1424
+ metadata: session.client.getMetadataSnapshot?.() ?? null,
1425
+ queuedTurn: message.mode.happyOrg,
1426
+ responseText: accumulatedResponse,
1427
+ turnStatus
1428
+ });
1429
+ if (finalizedTurn.nextMetadata && typeof session.client.updateMetadata === "function") {
1430
+ session.client.updateMetadata(() => finalizedTurn.nextMetadata);
1431
+ }
1432
+ if (finalizedTurn.report) {
1433
+ emitTurnReport(finalizedTurn.report);
1434
+ }
1435
+ if (finalizedTurn.terminateMessage) {
1436
+ emitStatusMessage(finalizedTurn.terminateMessage);
1437
+ }
1438
+ accumulatedResponse = finalizedTurn.cleanedText;
1439
+ emitFinalAssistantMessage(finalizedTurn.cleanedText);
1409
1440
  if (shouldClearHistoryAfterTurn) {
1410
1441
  conversationHistory.clear();
1411
1442
  emitStatusMessage("Compaction completed");
@@ -3130,8 +3161,28 @@ function resolveClaudeQueuedMessage(message, currentState) {
3130
3161
  function bindClaudeUserMessageQueue(opts) {
3131
3162
  let currentState = { ...opts.initialState };
3132
3163
  opts.session.onUserMessage((message) => {
3164
+ const happyOrgResult = opts.happyOrg ? registerKillSessionHandler.resolveHappyOrgQueuedTurn({
3165
+ metadata: opts.happyOrg.getMetadata(),
3166
+ message
3167
+ }) : {
3168
+ nextMetadata: null,
3169
+ queuedTurn: null,
3170
+ blocked: false,
3171
+ statusMessage: void 0
3172
+ };
3173
+ if (opts.happyOrg && happyOrgResult.nextMetadata) {
3174
+ opts.happyOrg.updateMetadata(() => happyOrgResult.nextMetadata);
3175
+ }
3176
+ if (happyOrgResult.blocked) {
3177
+ if (happyOrgResult.statusMessage) {
3178
+ opts.happyOrg?.emitStatusMessage(happyOrgResult.statusMessage);
3179
+ }
3180
+ api.logger.debugLargeJson("[claude] User message blocked by Happy Org runtime:", message);
3181
+ return;
3182
+ }
3133
3183
  const { nextState, queuedMessage } = resolveClaudeQueuedMessage(message, currentState);
3134
3184
  currentState = nextState;
3185
+ queuedMessage.mode.happyOrg = happyOrgResult.queuedTurn;
3135
3186
  if (queuedMessage.isolate) {
3136
3187
  api.logger.debug(`[claude] Queuing isolated special command: ${queuedMessage.specialCommand}`);
3137
3188
  opts.messageQueue.pushIsolateAndClear(queuedMessage.text, queuedMessage.mode);
@@ -3236,7 +3287,14 @@ async function runClaude(credentials, options = {}) {
3236
3287
  customSystemPrompt: mode.customSystemPrompt,
3237
3288
  appendSystemPrompt: mode.appendSystemPrompt,
3238
3289
  allowedTools: mode.allowedTools,
3239
- disallowedTools: mode.disallowedTools
3290
+ disallowedTools: mode.disallowedTools,
3291
+ happyOrg: mode.happyOrg ? {
3292
+ taskId: mode.happyOrg.context.taskId,
3293
+ organizationId: mode.happyOrg.context.organizationId,
3294
+ memberAgentId: mode.happyOrg.context.memberAgentId,
3295
+ supervisorAgentId: mode.happyOrg.context.supervisorAgentId,
3296
+ reopenContext: mode.happyOrg.reopenContext ?? null
3297
+ } : null
3240
3298
  }));
3241
3299
  bindClaudeUserMessageQueue({
3242
3300
  session,
@@ -3244,6 +3302,11 @@ async function runClaude(credentials, options = {}) {
3244
3302
  initialState: {
3245
3303
  permissionMode: options.permissionMode,
3246
3304
  model: options.model
3305
+ },
3306
+ happyOrg: {
3307
+ getMetadata: () => session.getMetadataSnapshot(),
3308
+ updateMetadata: (handler) => session.updateMetadata(handler),
3309
+ emitStatusMessage: (message) => session.sendSessionEvent({ type: "message", message })
3247
3310
  }
3248
3311
  });
3249
3312
  const cleanup = async () => {