happy-imou-cloud 2.1.41 → 2.1.43

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 (25) hide show
  1. package/dist/{BaseReasoningProcessor-Dica2zdX.mjs → BaseReasoningProcessor-CWCPAVJ3.mjs} +4 -2
  2. package/dist/{BaseReasoningProcessor-DAPsauUX.cjs → BaseReasoningProcessor-D6itItnT.cjs} +4 -2
  3. package/dist/{ProviderSelectionHandler-4nuTd8AP.cjs → ProviderSelectionHandler-CXwqOJpC.cjs} +2 -2
  4. package/dist/{ProviderSelectionHandler-CmeZ92yO.mjs → ProviderSelectionHandler-CYUH1U8F.mjs} +2 -2
  5. package/dist/{api-CwNEbJBM.mjs → api-B89O-SC-.mjs} +398 -43
  6. package/dist/{api-C_Tnx4UG.cjs → api-zMic8QXq.cjs} +399 -42
  7. package/dist/{command-D0FxDynx.mjs → command-W01qXC9C.mjs} +2 -2
  8. package/dist/{command-C3RwwPVD.cjs → command-_qNFoJ-l.cjs} +2 -2
  9. package/dist/{index-D5m2Duxd.mjs → index-CiHiCC31.mjs} +31 -16
  10. package/dist/{index-Dh0qGrEd.cjs → index-DVVbyQGZ.cjs} +34 -19
  11. package/dist/index.cjs +2 -2
  12. package/dist/index.mjs +2 -2
  13. package/dist/lib.cjs +1 -1
  14. package/dist/lib.d.cts +1041 -214
  15. package/dist/lib.d.mts +1041 -214
  16. package/dist/lib.mjs +1 -1
  17. package/dist/{registerKillSessionHandler-6JloVYkb.mjs → registerKillSessionHandler-BnS3ozFX.mjs} +274 -101
  18. package/dist/{registerKillSessionHandler-Bkh8uLhC.cjs → registerKillSessionHandler-CDYAY5Ev.cjs} +274 -101
  19. package/dist/{runClaude-Bng_IsE7.mjs → runClaude-0U7kV07_.mjs} +13 -4
  20. package/dist/{runClaude-mIFO7YxF.cjs → runClaude-C1xfptKt.cjs} +14 -5
  21. package/dist/{runCodex-cNuxGshy.cjs → runCodex-BBgKpYLO.cjs} +20 -6
  22. package/dist/{runCodex-DAc6flez.mjs → runCodex-DnDmHm7E.mjs} +19 -5
  23. package/dist/{runGemini-DsRFV8WN.cjs → runGemini-CUwQHJ9y.cjs} +21 -5
  24. package/dist/{runGemini-DdXj8ltI.mjs → runGemini-DcsIBQSc.mjs} +20 -4
  25. package/package.json +1 -1
@@ -18,7 +18,7 @@ import { spawn } from 'node:child_process';
18
18
  import { Expo } from 'expo-server-sdk';
19
19
 
20
20
  var name = "happy-imou-cloud";
21
- var version = "2.1.41";
21
+ var version = "2.1.43";
22
22
  var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
23
23
  var author = "long.zhu";
24
24
  var license = "MIT";
@@ -1657,12 +1657,29 @@ const UserMessageSchema = z$1.object({
1657
1657
  // Mobile messages include this
1658
1658
  meta: MessageMetaSchema.optional()
1659
1659
  });
1660
+ const AgentProviderSchema = z$1.enum(["gemini", "codex", "claude", "opencode"]);
1660
1661
  const AgentMessageSchema = z$1.object({
1661
1662
  role: z$1.literal("agent"),
1662
- content: z$1.object({
1663
- type: z$1.literal("output"),
1664
- data: z$1.any()
1665
- }),
1663
+ content: z$1.union([
1664
+ z$1.object({
1665
+ type: z$1.literal("output"),
1666
+ data: z$1.unknown()
1667
+ }),
1668
+ z$1.object({
1669
+ type: z$1.literal("codex"),
1670
+ data: z$1.unknown()
1671
+ }),
1672
+ z$1.object({
1673
+ type: z$1.literal("acp"),
1674
+ provider: AgentProviderSchema,
1675
+ data: z$1.unknown()
1676
+ }),
1677
+ z$1.object({
1678
+ type: z$1.literal("event"),
1679
+ id: z$1.string().optional(),
1680
+ data: z$1.unknown()
1681
+ })
1682
+ ]),
1666
1683
  meta: MessageMetaSchema.optional()
1667
1684
  });
1668
1685
  const MessageContentSchema = z$1.union([UserMessageSchema, AgentMessageSchema]);
@@ -1682,6 +1699,59 @@ function isNeedsHandoffTask(task) {
1682
1699
  }
1683
1700
  return false;
1684
1701
  }
1702
+ function resolveRuntimeSessionId(task) {
1703
+ return task.specialistHomeSessionId ?? null;
1704
+ }
1705
+ function resolveMachineId(task) {
1706
+ return task.specialistHomeMachineId ?? null;
1707
+ }
1708
+ function resolveTaskMergeState(task) {
1709
+ const runtimeSessionId = resolveRuntimeSessionId(task);
1710
+ const machineId = resolveMachineId(task);
1711
+ if (task.accessChannelState === "runtime_replaced") {
1712
+ return "conflict";
1713
+ }
1714
+ if (task.activeOwnerAgentId && task.activeOwnerAgentId !== task.ownerAgentId) {
1715
+ return "conflict";
1716
+ }
1717
+ if (task.accessChannelState === "reattach_required") {
1718
+ return "merge_pending";
1719
+ }
1720
+ if (task.hasActiveOwner === false && task.status !== "done" && task.status !== "terminated") {
1721
+ return "merge_pending";
1722
+ }
1723
+ if ((task.status === "active" || task.status === "waiting_close") && (!runtimeSessionId || !machineId)) {
1724
+ return "merge_pending";
1725
+ }
1726
+ return "stable";
1727
+ }
1728
+ function resolveTaskMergeStateSummary(task, mergeState, runtimeSessionId, machineId) {
1729
+ if (mergeState === "conflict") {
1730
+ if (task.accessChannelState === "runtime_replaced") {
1731
+ return "runtime was replaced before the merge view stabilized";
1732
+ }
1733
+ if (task.activeOwnerAgentId && task.activeOwnerAgentId !== task.ownerAgentId) {
1734
+ return `active owner moved to ${task.activeOwnerAgentId}`;
1735
+ }
1736
+ return "merge conflict needs owner review";
1737
+ }
1738
+ if (mergeState === "merge_pending") {
1739
+ if (task.accessChannelState === "reattach_required") {
1740
+ return "runtime reattach required before merge becomes stable";
1741
+ }
1742
+ if (task.hasActiveOwner === false) {
1743
+ return "no active owner is attached to the task yet";
1744
+ }
1745
+ if (!runtimeSessionId || !machineId) {
1746
+ return "runtime or machine attribution is still missing";
1747
+ }
1748
+ return "merge visibility is still pending";
1749
+ }
1750
+ if (runtimeSessionId && machineId) {
1751
+ return `runtime ${runtimeSessionId} on machine ${machineId}`;
1752
+ }
1753
+ return "merge is stable";
1754
+ }
1685
1755
  function classifyHappyOrgTaskBoardSection(task) {
1686
1756
  if (task.status === "done") {
1687
1757
  return "closed";
@@ -1695,8 +1765,12 @@ function classifyHappyOrgTaskBoardSection(task) {
1695
1765
  return "in_progress";
1696
1766
  }
1697
1767
  function buildHappyOrgTaskBoardView(tasks) {
1768
+ const organizationManifestId = tasks.find((task) => task.organizationManifestId)?.organizationManifestId ?? null;
1698
1769
  const items = [...tasks].filter((task) => task.status !== "terminated").sort((left, right) => right.updatedAt - left.updatedAt || left.taskId.localeCompare(right.taskId)).map((task) => {
1699
1770
  const sectionKey = classifyHappyOrgTaskBoardSection(task);
1771
+ const runtimeSessionId = resolveRuntimeSessionId(task);
1772
+ const machineId = resolveMachineId(task);
1773
+ const mergeState = resolveTaskMergeState(task);
1700
1774
  return {
1701
1775
  taskId: task.taskId,
1702
1776
  title: task.title,
@@ -1705,8 +1779,15 @@ function buildHappyOrgTaskBoardView(tasks) {
1705
1779
  lane: sectionKey,
1706
1780
  ownerAgentId: task.ownerAgentId,
1707
1781
  ownerName: task.ownerName,
1782
+ organizationManifestId: task.organizationManifestId ?? null,
1783
+ positionId: task.positionId ?? null,
1708
1784
  responsibilityId: task.responsibilityId ?? null,
1709
1785
  responsibilityLabel: task.responsibilityLabel ?? null,
1786
+ runtimeSessionId,
1787
+ machineId,
1788
+ accessChannelState: task.accessChannelState ?? null,
1789
+ mergeState,
1790
+ mergeStateSummary: resolveTaskMergeStateSummary(task, mergeState, runtimeSessionId, machineId),
1710
1791
  currentProgress: task.positionStatus || task.summary || null,
1711
1792
  latestResult: task.latestUserVisibleResult || task.reviewSummary || task.resultSummary || null,
1712
1793
  blockerOrDecision: task.blockerSummary || task.blocker || task.decisionNeeded || task.adviceSummary || task.ceoWriteNextStep || null,
@@ -1730,6 +1811,7 @@ function buildHappyOrgTaskBoardView(tasks) {
1730
1811
  { key: "closed", count: buckets.closed.length, items: buckets.closed }
1731
1812
  ];
1732
1813
  return {
1814
+ organizationManifestId,
1733
1815
  totalCount: items.length,
1734
1816
  inProgressCount: sections[0].count,
1735
1817
  handoffCount: sections[1].count,
@@ -1740,6 +1822,49 @@ function buildHappyOrgTaskBoardView(tasks) {
1740
1822
  };
1741
1823
  }
1742
1824
 
1825
+ const HTTP_URL_PATTERN = /^https?:\/\//i;
1826
+ const FILE_URL_PATTERN = /^file:\/\//i;
1827
+ const FILE_EXTENSION_PATTERN = /\.[A-Za-z0-9_-]{1,16}$/;
1828
+ const SPECIAL_FILE_NAME_PATTERN = /(^|[\\/])(Dockerfile|Makefile|README(?:\.[A-Za-z0-9_-]+)?)$/i;
1829
+ function isLikelyPreviewableArtifactTarget(target) {
1830
+ const trimmed = typeof target === "string" ? target.trim() : "";
1831
+ if (!trimmed) {
1832
+ return false;
1833
+ }
1834
+ if (HTTP_URL_PATTERN.test(trimmed) || FILE_URL_PATTERN.test(trimmed)) {
1835
+ return true;
1836
+ }
1837
+ const stripped = trimmed.split(/[?#]/)[0] ?? trimmed;
1838
+ const basename = stripped.split(/[\\/]/).pop() ?? stripped;
1839
+ if (!basename) {
1840
+ return false;
1841
+ }
1842
+ return FILE_EXTENSION_PATTERN.test(basename) || SPECIAL_FILE_NAME_PATTERN.test(basename);
1843
+ }
1844
+ function resolveArtifactTarget(target) {
1845
+ const trimmed = typeof target === "string" ? target.trim() : "";
1846
+ if (!trimmed || !isLikelyPreviewableArtifactTarget(trimmed)) {
1847
+ return {
1848
+ kind: "unknown",
1849
+ target: trimmed
1850
+ };
1851
+ }
1852
+ if (HTTP_URL_PATTERN.test(trimmed) || FILE_URL_PATTERN.test(trimmed)) {
1853
+ return {
1854
+ kind: "external",
1855
+ target: trimmed
1856
+ };
1857
+ }
1858
+ return {
1859
+ kind: "path",
1860
+ target: trimmed
1861
+ };
1862
+ }
1863
+ function normalizePreviewableArtifactTarget(target) {
1864
+ const resolved = resolveArtifactTarget(target);
1865
+ return resolved.kind === "unknown" ? null : resolved.target;
1866
+ }
1867
+
1743
1868
  const HAPPY_ORG_LOCAL_REPO_SCHEMA_VERSION = "1.7.3-local-repo-v1";
1744
1869
  const HAPPY_ORG_WRITER_LOCK_SCHEMA_VERSION = "1.7.3-writer-lock-v1";
1745
1870
  const WRITER_LOCK_LEASE_DURATION_SECONDS = 900;
@@ -1781,6 +1906,12 @@ const GoalSchema = z$1.object({
1781
1906
  approvedBy: z$1.string().nullable(),
1782
1907
  updatedAt: z$1.number()
1783
1908
  });
1909
+ const OrganizationValueSchema = z$1.object({
1910
+ name: z$1.string(),
1911
+ slug: z$1.string().min(1),
1912
+ summary: z$1.string().nullable(),
1913
+ createdAt: z$1.number()
1914
+ });
1784
1915
  const TaskUpdatePayloadSchema = z$1.object({
1785
1916
  taskId: z$1.string().min(1),
1786
1917
  status: TaskStatusSchema,
@@ -2008,6 +2139,7 @@ const LogValueSchema = z$1.object({
2008
2139
  taskId: z$1.string(),
2009
2140
  entries: z$1.array(LogEntrySchema)
2010
2141
  });
2142
+ const OrganizationEnvelopeSchema = RevisionEnvelopeSchema(OrganizationValueSchema);
2011
2143
  const GoalEnvelopeSchema = RevisionEnvelopeSchema(GoalSchema);
2012
2144
  const PositionEnvelopeSchema = RevisionEnvelopeSchema(PositionValueSchema);
2013
2145
  const WorkspaceEnvelopeSchema = RevisionEnvelopeSchema(WorkspaceValueSchema);
@@ -2304,20 +2436,31 @@ async function readHappyOrgDispatchTruthSnapshot(rootPath) {
2304
2436
  };
2305
2437
  }
2306
2438
  async function readHappyOrgRepoTaskBoard(rootPath) {
2307
- const [taskIds, positions, responsibilities] = await Promise.all([
2439
+ const [taskIds, positions, responsibilities, organization] = await Promise.all([
2308
2440
  listTaskIds(rootPath),
2309
2441
  listPositionEnvelopes(rootPath),
2310
- readResponsibilities(rootPath)
2442
+ readResponsibilities(rootPath),
2443
+ readEnvelope(path.join(rootPath, "ORGANIZATION.json"), OrganizationEnvelopeSchema)
2311
2444
  ]);
2445
+ const organizationManifestId = organization?.value.slug ?? null;
2312
2446
  const responsibilityLabelById = /* @__PURE__ */ new Map();
2447
+ const responsibilityPositionIdById = /* @__PURE__ */ new Map();
2448
+ const positionIdByAgentId = /* @__PURE__ */ new Map();
2313
2449
  for (const responsibility of responsibilities?.value ?? []) {
2314
2450
  responsibilityLabelById.set(responsibility.responsibilityId, responsibility.title);
2451
+ responsibilityPositionIdById.set(responsibility.responsibilityId, responsibility.positionId);
2315
2452
  }
2316
2453
  for (const position of positions) {
2317
2454
  const fallbackResponsibilityId = position.value.responsibilityIds[0] ?? buildResponsibilityId(position.value.slug);
2318
2455
  if (!responsibilityLabelById.has(fallbackResponsibilityId)) {
2319
2456
  responsibilityLabelById.set(fallbackResponsibilityId, position.value.label || position.value.agentName || position.value.slug);
2320
2457
  }
2458
+ if (!responsibilityPositionIdById.has(fallbackResponsibilityId)) {
2459
+ responsibilityPositionIdById.set(fallbackResponsibilityId, position.value.positionId);
2460
+ }
2461
+ if (position.value.agentId) {
2462
+ positionIdByAgentId.set(position.value.agentId, position.value.positionId);
2463
+ }
2321
2464
  }
2322
2465
  const tasks = await Promise.all(taskIds.map(async (taskId) => {
2323
2466
  const bundle = await loadTaskEnvelope(rootPath, taskId);
@@ -2325,6 +2468,8 @@ async function readHappyOrgRepoTaskBoard(rootPath) {
2325
2468
  }));
2326
2469
  return buildHappyOrgTaskBoardView(tasks.filter((task) => task !== null).map((task) => ({
2327
2470
  ...task,
2471
+ organizationManifestId,
2472
+ positionId: (task.responsibilityId ? responsibilityPositionIdById.get(task.responsibilityId) ?? null : null) ?? positionIdByAgentId.get(task.activeOwnerAgentId ?? "") ?? positionIdByAgentId.get(task.ownerAgentId) ?? null,
2328
2473
  responsibilityLabel: task.responsibilityLabel ?? (task.responsibilityId ? responsibilityLabelById.get(task.responsibilityId) ?? null : null)
2329
2474
  })));
2330
2475
  }
@@ -3017,6 +3162,10 @@ async function recordHappyOrgTurnReport(options) {
3017
3162
  }
3018
3163
  const now = Date.now();
3019
3164
  const nextStatus = nextStatusFromTurnReport(options.report);
3165
+ const artifactReference = normalizePreviewableArtifactTarget(options.report.targetArtifact);
3166
+ const artifactTarget = resolveArtifactTarget(options.report.targetArtifact);
3167
+ const nextPath = artifactTarget.kind === "path" ? artifactTarget.target : bundle.task.value.path;
3168
+ const nextPortablePath = artifactTarget.kind === "path" ? normalizePortablePath(artifactTarget.target) : bundle.task.value.portablePath;
3020
3169
  const nextTaskValue = {
3021
3170
  ...bundle.task.value,
3022
3171
  status: nextStatus,
@@ -3044,8 +3193,8 @@ async function recordHappyOrgTurnReport(options) {
3044
3193
  blockerSummary: options.report.acceptanceHandoff?.blockerSummary ?? bundle.task.value.blockerSummary ?? null,
3045
3194
  acceptanceState: options.report.acceptanceHandoff?.acceptanceState ?? bundle.task.value.acceptanceState ?? null,
3046
3195
  ceoWriteNextStep: options.report.acceptanceHandoff?.ceoWriteNextStep ?? bundle.task.value.ceoWriteNextStep ?? null,
3047
- path: options.report.targetArtifact ?? bundle.task.value.path,
3048
- portablePath: normalizePortablePath(options.report.targetArtifact ?? bundle.task.value.path)
3196
+ path: nextPath,
3197
+ portablePath: nextPortablePath
3049
3198
  };
3050
3199
  const taskRoot = buildTaskRoot(options.rootPath, options.report.taskId);
3051
3200
  await writeEnvelope(path.join(taskRoot, "TASK.json"), {
@@ -3084,7 +3233,7 @@ async function recordHappyOrgTurnReport(options) {
3084
3233
  reviewSummary: bundle.result?.value.reviewSummary ?? null,
3085
3234
  assetCaptureStatus: bundle.result?.value.assetCaptureStatus ?? null,
3086
3235
  assetCaptureSummary: bundle.result?.value.assetCaptureSummary ?? null,
3087
- assetReference: options.report.targetArtifact ?? bundle.result?.value.assetReference ?? null,
3236
+ assetReference: artifactReference ?? bundle.result?.value.assetReference ?? null,
3088
3237
  closedAt: nextStatus === "waiting_close" ? now : bundle.result?.value.closedAt ?? null,
3089
3238
  updatedAt: now,
3090
3239
  updatedBy: options.report.memberAgentId
@@ -3515,6 +3664,12 @@ async function runShellCommand(opts) {
3515
3664
  });
3516
3665
  }
3517
3666
 
3667
+ function quoteShellArgument(value) {
3668
+ if (value.length === 0) {
3669
+ return "''";
3670
+ }
3671
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
3672
+ }
3518
3673
  function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
3519
3674
  rpcHandlerManager.registerHandler("bash", async (data) => {
3520
3675
  logger.debug("Shell command request:", data.command);
@@ -3560,6 +3715,52 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
3560
3715
  return result;
3561
3716
  }
3562
3717
  });
3718
+ rpcHandlerManager.registerHandler("ripgrep", async (data) => {
3719
+ logger.debug("Ripgrep request:", data.args);
3720
+ if (data.cwd) {
3721
+ const validation = validatePath(data.cwd, workingDirectory);
3722
+ if (!validation.valid) {
3723
+ return { success: false, error: validation.error };
3724
+ }
3725
+ }
3726
+ try {
3727
+ const cwd = data.cwd || workingDirectory;
3728
+ const command = ["rg", ...data.args.map(quoteShellArgument)].join(" ");
3729
+ logger.debug("Ripgrep executing...", {
3730
+ cwd,
3731
+ timeout: data.timeout || 3e4,
3732
+ args: data.args
3733
+ });
3734
+ const result = await runShellCommand({
3735
+ command,
3736
+ cwd,
3737
+ timeoutMs: data.timeout || 3e4
3738
+ });
3739
+ logger.debug("Ripgrep finished:", {
3740
+ success: result.success,
3741
+ exitCode: result.exitCode,
3742
+ error: result.error,
3743
+ stdoutLen: result.stdout.length,
3744
+ stderrLen: result.stderr.length
3745
+ });
3746
+ return result;
3747
+ } catch (error) {
3748
+ const result = {
3749
+ success: false,
3750
+ stdout: "",
3751
+ stderr: "",
3752
+ exitCode: 1,
3753
+ error: error instanceof Error ? error.message : "Ripgrep failed"
3754
+ };
3755
+ logger.debug("Ripgrep failed before execution completed:", {
3756
+ cwd: data.cwd || workingDirectory,
3757
+ success: false,
3758
+ exitCode: result.exitCode,
3759
+ error: result.error
3760
+ });
3761
+ return result;
3762
+ }
3763
+ });
3563
3764
  rpcHandlerManager.registerHandler("readFile", async (data) => {
3564
3765
  logger.debug("Read file request:", data.path);
3565
3766
  const validation = validatePath(data.path, workingDirectory);
@@ -4122,9 +4323,95 @@ function isTransientMessageStreamDelta(body) {
4122
4323
  return record.type === "message" && record.phase === "delta";
4123
4324
  }
4124
4325
 
4326
+ function deterministicStringify(obj, options = {}) {
4327
+ const {
4328
+ undefinedBehavior = "omit",
4329
+ sortArrays = false,
4330
+ replacer,
4331
+ includeSymbols = false
4332
+ } = options;
4333
+ const seen = /* @__PURE__ */ new WeakSet();
4334
+ function processValue(value, key) {
4335
+ if (replacer && key !== void 0) {
4336
+ value = replacer(key, value);
4337
+ }
4338
+ if (value === null) return null;
4339
+ if (value === void 0) {
4340
+ switch (undefinedBehavior) {
4341
+ case "omit":
4342
+ return void 0;
4343
+ case "null":
4344
+ return null;
4345
+ case "throw":
4346
+ throw new Error(`Undefined value at key: ${key}`);
4347
+ }
4348
+ }
4349
+ if (typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
4350
+ return value;
4351
+ }
4352
+ if (value instanceof Date) {
4353
+ return value.toISOString();
4354
+ }
4355
+ if (value instanceof RegExp) {
4356
+ return value.toString();
4357
+ }
4358
+ if (typeof value === "function") {
4359
+ return void 0;
4360
+ }
4361
+ if (typeof value === "symbol") {
4362
+ return includeSymbols ? value.toString() : void 0;
4363
+ }
4364
+ if (typeof value === "bigint") {
4365
+ return value.toString() + "n";
4366
+ }
4367
+ if (seen.has(value)) {
4368
+ throw new Error("Circular reference detected");
4369
+ }
4370
+ seen.add(value);
4371
+ if (Array.isArray(value)) {
4372
+ const processed2 = value.map((item, index) => processValue(item, String(index))).filter((item) => item !== void 0);
4373
+ if (sortArrays) {
4374
+ processed2.sort((a, b) => {
4375
+ const aStr = JSON.stringify(processValue(a));
4376
+ const bStr = JSON.stringify(processValue(b));
4377
+ return aStr.localeCompare(bStr);
4378
+ });
4379
+ }
4380
+ seen.delete(value);
4381
+ return processed2;
4382
+ }
4383
+ if (value.constructor === Object || value.constructor === void 0) {
4384
+ const processed2 = {};
4385
+ const keys = Object.keys(value).sort();
4386
+ for (const k of keys) {
4387
+ const processedValue = processValue(value[k], k);
4388
+ if (processedValue !== void 0) {
4389
+ processed2[k] = processedValue;
4390
+ }
4391
+ }
4392
+ seen.delete(value);
4393
+ return processed2;
4394
+ }
4395
+ try {
4396
+ const plain = { ...value };
4397
+ seen.delete(value);
4398
+ return processValue(plain, key);
4399
+ } catch {
4400
+ seen.delete(value);
4401
+ return String(value);
4402
+ }
4403
+ }
4404
+ const processed = processValue(obj);
4405
+ return JSON.stringify(processed);
4406
+ }
4407
+ function hashObject(obj, options, encoding = "hex") {
4408
+ const jsonString = deterministicStringify(obj, options);
4409
+ return createHash("sha256").update(jsonString).digest(encoding);
4410
+ }
4411
+
4125
4412
  const MAX_PENDING_RELIABLE_SESSION_MESSAGES = 200;
4126
4413
  const MAX_PENDING_RELIABLE_SESSION_MESSAGE_BYTES = 512 * 1024;
4127
- const PROTOCOL_V3_INITIAL_SNAPSHOT_LIMIT = 150;
4414
+ const PROTOCOL_V3_INITIAL_SNAPSHOT_LIMIT = 300;
4128
4415
  const PROTOCOL_V3_CHANGES_PAGE_LIMIT = 200;
4129
4416
  const PROTOCOL_V3_CAPABILITIES_WAIT_MS = 250;
4130
4417
  const COMMITTED_SESSION_WRITE_ACK_TIMEOUT_MS = 5e3;
@@ -4257,8 +4544,11 @@ class ApiSessionClient extends EventEmitter {
4257
4544
  agentState;
4258
4545
  agentStateVersion;
4259
4546
  socket;
4547
+ initialSessionSeq;
4260
4548
  pendingMessages = [];
4261
4549
  pendingMessageCallback = null;
4550
+ pendingSessionMessages = [];
4551
+ pendingSessionMessageCallback = null;
4262
4552
  rpcHandlerManager;
4263
4553
  agentStateLock = new AsyncLock();
4264
4554
  metadataLock = new AsyncLock();
@@ -4298,6 +4588,7 @@ class ApiSessionClient extends EventEmitter {
4298
4588
  this.agentStateVersion = session.agentStateVersion;
4299
4589
  this.encryptionKey = session.encryptionKey;
4300
4590
  this.encryptionVariant = session.encryptionVariant;
4591
+ this.initialSessionSeq = session.seq;
4301
4592
  this.lastChangeSeq = session.seq;
4302
4593
  this.committedSessionWriteAckMode = this.credentials.signing ? "unknown" : "unsupported";
4303
4594
  for (const [dispatchId, ack] of Object.entries(session.metadata?.happyOrg?.dispatchAcks ?? {})) {
@@ -4412,6 +4703,12 @@ class ApiSessionClient extends EventEmitter {
4412
4703
  callback(this.pendingMessages.shift());
4413
4704
  }
4414
4705
  }
4706
+ onSessionMessage(callback) {
4707
+ this.pendingSessionMessageCallback = callback;
4708
+ while (this.pendingSessionMessages.length > 0) {
4709
+ callback(this.pendingSessionMessages.shift());
4710
+ }
4711
+ }
4415
4712
  configureProtocolV3Sync(options) {
4416
4713
  this.invalidateProtocolV3SessionSync();
4417
4714
  this.protocolV3SessionSync = options;
@@ -4438,6 +4735,13 @@ class ApiSessionClient extends EventEmitter {
4438
4735
  }
4439
4736
  return preserveSessionRuntimeMetadata(this.metadata, next);
4440
4737
  }
4738
+ didSessionValueChange(current, next) {
4739
+ try {
4740
+ return deterministicStringify(current ?? null) !== deterministicStringify(next ?? null);
4741
+ } catch {
4742
+ return true;
4743
+ }
4744
+ }
4441
4745
  async waitForMetadataUpdate(signal) {
4442
4746
  if (signal?.aborted) {
4443
4747
  throw createAbortError();
@@ -4690,7 +4994,10 @@ class ApiSessionClient extends EventEmitter {
4690
4994
  updateMetadata(handler) {
4691
4995
  this.trackPendingBackgroundTask(this.metadataLock.inLock(async () => {
4692
4996
  await backoff(async () => {
4693
- let updated = this.mergeRuntimeMetadata(handler(this.metadata));
4997
+ const updated = this.mergeRuntimeMetadata(handler(this.metadata));
4998
+ if (!this.didSessionValueChange(this.metadata, updated)) {
4999
+ return;
5000
+ }
4694
5001
  const sessionIndex = buildSessionRuntimeIndex(updated);
4695
5002
  const answer = await this.socket.emitWithAck("update-metadata", {
4696
5003
  sid: this.sessionId,
@@ -4736,7 +5043,10 @@ class ApiSessionClient extends EventEmitter {
4736
5043
  logger.debugLargeJson("Updating agent state", this.agentState);
4737
5044
  this.trackPendingBackgroundTask(this.agentStateLock.inLock(async () => {
4738
5045
  await backoff(async () => {
4739
- let updated = handler(this.agentState || {});
5046
+ const updated = handler(this.agentState || {});
5047
+ if (!this.didSessionValueChange(this.agentState, updated)) {
5048
+ return;
5049
+ }
4740
5050
  const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) : null });
4741
5051
  if (answer.result === "success") {
4742
5052
  this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
@@ -4818,7 +5128,10 @@ class ApiSessionClient extends EventEmitter {
4818
5128
  if (messageSeq !== null && messageSeq <= this.lastChangeSeq) {
4819
5129
  return;
4820
5130
  }
4821
- this.dispatchPlaintextSessionMessage(data.body.message.content.p);
5131
+ this.dispatchPlaintextSessionMessage(data.body.message.content.p, {
5132
+ source: "live",
5133
+ seq: messageSeq
5134
+ });
4822
5135
  if (messageSeq !== null) {
4823
5136
  this.lastChangeSeq = Math.max(this.lastChangeSeq, messageSeq);
4824
5137
  }
@@ -4829,7 +5142,10 @@ class ApiSessionClient extends EventEmitter {
4829
5142
  if (messageSeq !== null && messageSeq <= this.lastChangeSeq) {
4830
5143
  return;
4831
5144
  }
4832
- this.dispatchEncryptedSessionMessage(data.body.message.content.c);
5145
+ this.dispatchEncryptedSessionMessage(data.body.message.content.c, {
5146
+ source: "live",
5147
+ seq: messageSeq
5148
+ });
4833
5149
  if (messageSeq !== null) {
4834
5150
  this.lastChangeSeq = Math.max(this.lastChangeSeq, messageSeq);
4835
5151
  }
@@ -4874,18 +5190,25 @@ class ApiSessionClient extends EventEmitter {
4874
5190
  }
4875
5191
  this.emit("message", data.body);
4876
5192
  }
4877
- dispatchEncryptedSessionMessage(encryptedMessage) {
5193
+ dispatchEncryptedSessionMessage(encryptedMessage, options) {
4878
5194
  const body = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(encryptedMessage));
4879
5195
  logger.debugLargeJson("[SOCKET] [UPDATE] Received update:", body);
4880
5196
  const parsedBody = body ?? buildHappyOrgFallbackUserMessage(encryptedMessage);
4881
- const userResult = UserMessageSchema.safeParse(parsedBody);
4882
- if (userResult.success) {
4883
- this.maybeAutoAcknowledgeHappyOrgDispatch(userResult.data);
4884
- if (this.pendingMessageCallback) {
4885
- this.pendingMessageCallback(userResult.data);
4886
- } else {
4887
- this.pendingMessages.push(userResult.data);
5197
+ const messageResult = MessageContentSchema.safeParse(parsedBody);
5198
+ if (messageResult.success) {
5199
+ const queuesPendingInput = this.dispatchSessionTranscriptMessage(messageResult.data, options);
5200
+ if (messageResult.data.role === "user") {
5201
+ if (queuesPendingInput) {
5202
+ this.maybeAutoAcknowledgeHappyOrgDispatch(messageResult.data);
5203
+ if (this.pendingMessageCallback) {
5204
+ this.pendingMessageCallback(messageResult.data);
5205
+ } else {
5206
+ this.pendingMessages.push(messageResult.data);
5207
+ }
5208
+ }
5209
+ return;
4888
5210
  }
5211
+ this.emit("message", messageResult.data);
4889
5212
  return;
4890
5213
  }
4891
5214
  this.emit("message", parsedBody);
@@ -4894,7 +5217,7 @@ class ApiSessionClient extends EventEmitter {
4894
5217
  * Dispatch a plaintext session message (AES mode: server already decrypted).
4895
5218
  * The plaintext JSON is parsed and validated as a UserMessage.
4896
5219
  */
4897
- dispatchPlaintextSessionMessage(plaintextJson) {
5220
+ dispatchPlaintextSessionMessage(plaintextJson, options) {
4898
5221
  logger.debug("[SOCKET] [UPDATE] [PLAINTEXT] Received plaintext message");
4899
5222
  let parsedBody = null;
4900
5223
  try {
@@ -4903,19 +5226,41 @@ class ApiSessionClient extends EventEmitter {
4903
5226
  logger.debug("[SOCKET] [UPDATE] [PLAINTEXT] Failed to parse plaintext JSON, ignoring");
4904
5227
  return;
4905
5228
  }
4906
- const userResult = UserMessageSchema.safeParse(parsedBody);
4907
- if (userResult.success) {
4908
- this.maybeAutoAcknowledgeHappyOrgDispatch(userResult.data);
4909
- if (this.pendingMessageCallback) {
4910
- this.pendingMessageCallback(userResult.data);
4911
- } else {
4912
- this.pendingMessages.push(userResult.data);
5229
+ const messageResult = MessageContentSchema.safeParse(parsedBody);
5230
+ if (messageResult.success) {
5231
+ const queuesPendingInput = this.dispatchSessionTranscriptMessage(messageResult.data, options);
5232
+ if (messageResult.data.role === "user") {
5233
+ if (queuesPendingInput) {
5234
+ this.maybeAutoAcknowledgeHappyOrgDispatch(messageResult.data);
5235
+ if (this.pendingMessageCallback) {
5236
+ this.pendingMessageCallback(messageResult.data);
5237
+ } else {
5238
+ this.pendingMessages.push(messageResult.data);
5239
+ }
5240
+ }
5241
+ return;
4913
5242
  }
5243
+ this.emit("message", messageResult.data);
4914
5244
  return;
4915
5245
  }
4916
- logger.debug("[SOCKET] [UPDATE] [PLAINTEXT] Message did not match UserMessageSchema, emitting generic event");
5246
+ logger.debug("[SOCKET] [UPDATE] [PLAINTEXT] Message did not match MessageContentSchema, emitting generic event");
4917
5247
  this.emit("message", parsedBody);
4918
5248
  }
5249
+ dispatchSessionTranscriptMessage(message, options) {
5250
+ const queuesPendingInput = message.role === "user" && (options.source === "live" || this.initialSessionSeq === 0);
5251
+ const transcriptMessage = {
5252
+ source: options.source,
5253
+ seq: typeof options.seq === "number" ? options.seq : null,
5254
+ message,
5255
+ queuesPendingInput
5256
+ };
5257
+ if (this.pendingSessionMessageCallback) {
5258
+ this.pendingSessionMessageCallback(transcriptMessage);
5259
+ } else {
5260
+ this.pendingSessionMessages.push(transcriptMessage);
5261
+ }
5262
+ return queuesPendingInput;
5263
+ }
4919
5264
  maybeAutoAcknowledgeHappyOrgDispatch(message) {
4920
5265
  const candidate = this.resolveHappyOrgDispatchBusinessAckCandidate(message);
4921
5266
  if (!candidate) {
@@ -5220,7 +5565,7 @@ class ApiSessionClient extends EventEmitter {
5220
5565
  sessionId: this.sessionId,
5221
5566
  lastChangeSeq: this.lastChangeSeq
5222
5567
  });
5223
- if (!this.initialProtocolV3SnapshotComplete && this.lastChangeSeq === 0 && (!this.protocolV3Descriptor || this.supportsKnownProtocolCapability("session_snapshot_v3"))) {
5568
+ if (!this.initialProtocolV3SnapshotComplete && (!this.protocolV3Descriptor || this.supportsKnownProtocolCapability("session_snapshot_v3"))) {
5224
5569
  const snapshot = await this.protocolV3SessionSync.getSessionSnapshot(
5225
5570
  this.sessionId,
5226
5571
  this.protocolV3SessionSync.snapshotLimit ?? PROTOCOL_V3_INITIAL_SNAPSHOT_LIMIT
@@ -5277,13 +5622,16 @@ class ApiSessionClient extends EventEmitter {
5277
5622
  applyProtocolV3Snapshot(snapshot) {
5278
5623
  this.applyProtocolV3SessionOverlay(snapshot.session);
5279
5624
  for (const message of snapshot.snapshot.messages) {
5280
- if (message.seq <= this.lastChangeSeq) {
5281
- continue;
5282
- }
5283
5625
  if (message.content.t === "plaintext") {
5284
- this.dispatchPlaintextSessionMessage(message.content.p);
5626
+ this.dispatchPlaintextSessionMessage(message.content.p, {
5627
+ source: "snapshot",
5628
+ seq: message.seq
5629
+ });
5285
5630
  } else if (message.content.t === "encrypted") {
5286
- this.dispatchEncryptedSessionMessage(message.content.c);
5631
+ this.dispatchEncryptedSessionMessage(message.content.c, {
5632
+ source: "snapshot",
5633
+ seq: message.seq
5634
+ });
5287
5635
  }
5288
5636
  this.lastChangeSeq = Math.max(this.lastChangeSeq, message.seq);
5289
5637
  }
@@ -5312,11 +5660,18 @@ class ApiSessionClient extends EventEmitter {
5312
5660
  case "message.created": {
5313
5661
  const message = payload.message;
5314
5662
  if (message && typeof message === "object") {
5663
+ const messageSeq = typeof message.seq === "number" ? message.seq : null;
5315
5664
  const content = message.content;
5316
5665
  if (content && typeof content === "object" && content.t === "plaintext" && typeof content.p === "string") {
5317
- this.dispatchPlaintextSessionMessage(content.p);
5666
+ this.dispatchPlaintextSessionMessage(content.p, {
5667
+ source: "live",
5668
+ seq: messageSeq ?? change.changeSeq
5669
+ });
5318
5670
  } else if (content && typeof content === "object" && content.t === "encrypted" && typeof content.c === "string") {
5319
- this.dispatchEncryptedSessionMessage(content.c);
5671
+ this.dispatchEncryptedSessionMessage(content.c, {
5672
+ source: "live",
5673
+ seq: messageSeq ?? change.changeSeq
5674
+ });
5320
5675
  }
5321
5676
  }
5322
5677
  break;
@@ -5737,12 +6092,12 @@ class ApiMachineClient {
5737
6092
  requestShutdown
5738
6093
  }) {
5739
6094
  this.rpcHandlerManager.registerHandler("spawn-happy-session", async (params) => {
5740
- const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token, environmentVariables } = params || {};
6095
+ const { directory, sessionId, machineId, managedSessionTag, approvedNewDirectoryCreation, agent, token, resume, environmentVariables } = params || {};
5741
6096
  logger.debug(`[API MACHINE] Spawning session with params: ${JSON.stringify(params)}`);
5742
6097
  if (!directory) {
5743
6098
  throw new Error("Directory is required");
5744
6099
  }
5745
- const result = await spawnSession({ directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token, environmentVariables });
6100
+ const result = await spawnSession({ directory, sessionId, machineId, managedSessionTag, approvedNewDirectoryCreation, agent, token, resume, environmentVariables });
5746
6101
  switch (result.type) {
5747
6102
  case "success":
5748
6103
  logger.debug(`[API MACHINE] Spawned session ${result.sessionId}`);
@@ -6918,4 +7273,4 @@ var api = /*#__PURE__*/Object.freeze({
6918
7273
  ApiClient: ApiClient
6919
7274
  });
6920
7275
 
6921
- export { ApiClient as A, HAPPY_CLOUD_DAEMON_PORT as B, clearDaemonState as C, packageJson as D, acquireDaemonLock as E, writeDaemonState as F, releaseDaemonLock as G, HeadTailPreviewBuffer as H, validateProfileForAgent as I, getProfileEnvironmentVariables as J, clearCredentials as K, clearMachineId as L, readHappyOrgDispatchTruthSnapshot as M, processHappyOrgRepoRequests as N, readHappyOrgRepoTaskBoard as O, HappyOrgTurnReportSchema as P, recordHappyOrgTurnReport as Q, MessageContentSchema as R, buildSocketAuth as S, encrypt as T, getLatestDaemonLog as U, persistence as V, api as W, ApiSessionClient as a, connectionState as b, configuration as c, AssistantMessageStream as d, HAPPY_ORG_REPLY_ACK_VERSION as e, HAPPY_ORG_TURN_REPORT_TAG as f, HAPPY_ORG_SUMMARY_MAX_LENGTH as g, HAPPY_ORG_REPEAT_THRESHOLD as h, isAuthenticationRequiredError as i, backoff as j, delay as k, logger as l, AsyncLock as m, encodeBase64 as n, readCredentials as o, preserveSessionRuntimeMetadata as p, ensureSigningCredentials as q, readSettings as r, startOfflineReconnection as s, encodeBase64Url as t, updateSettings as u, buildClientHeaders as v, decodeBase64 as w, writeCredentialsLegacy as x, writeCredentialsDataKey as y, readDaemonState as z };
7276
+ export { ApiClient as A, writeCredentialsDataKey as B, readDaemonState as C, HAPPY_CLOUD_DAEMON_PORT as D, clearDaemonState as E, packageJson as F, acquireDaemonLock as G, HeadTailPreviewBuffer as H, writeDaemonState as I, releaseDaemonLock as J, validateProfileForAgent as K, getProfileEnvironmentVariables as L, clearCredentials as M, clearMachineId as N, readHappyOrgDispatchTruthSnapshot as O, processHappyOrgRepoRequests as P, readHappyOrgRepoTaskBoard as Q, HappyOrgTurnReportSchema as R, recordHappyOrgTurnReport as S, MessageContentSchema as T, buildSocketAuth as U, encrypt as V, getLatestDaemonLog as W, persistence as X, api as Y, ApiSessionClient as a, connectionState as b, configuration as c, AssistantMessageStream as d, HAPPY_ORG_REPLY_ACK_VERSION as e, HAPPY_ORG_TURN_REPORT_TAG as f, HAPPY_ORG_SUMMARY_MAX_LENGTH as g, hashObject as h, isAuthenticationRequiredError as i, HAPPY_ORG_REPEAT_THRESHOLD as j, backoff as k, logger as l, delay as m, normalizePreviewableArtifactTarget as n, AsyncLock as o, preserveSessionRuntimeMetadata as p, encodeBase64 as q, readSettings as r, startOfflineReconnection as s, readCredentials as t, ensureSigningCredentials as u, updateSettings as v, encodeBase64Url as w, buildClientHeaders as x, decodeBase64 as y, writeCredentialsLegacy as z };