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
@@ -38,7 +38,7 @@ function _interopNamespaceDefault(e) {
38
38
  var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
39
39
 
40
40
  var name = "happy-imou-cloud";
41
- var version = "2.1.41";
41
+ var version = "2.1.43";
42
42
  var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
43
43
  var author = "long.zhu";
44
44
  var license = "MIT";
@@ -1677,12 +1677,29 @@ const UserMessageSchema = z.z.object({
1677
1677
  // Mobile messages include this
1678
1678
  meta: MessageMetaSchema.optional()
1679
1679
  });
1680
+ const AgentProviderSchema = z.z.enum(["gemini", "codex", "claude", "opencode"]);
1680
1681
  const AgentMessageSchema = z.z.object({
1681
1682
  role: z.z.literal("agent"),
1682
- content: z.z.object({
1683
- type: z.z.literal("output"),
1684
- data: z.z.any()
1685
- }),
1683
+ content: z.z.union([
1684
+ z.z.object({
1685
+ type: z.z.literal("output"),
1686
+ data: z.z.unknown()
1687
+ }),
1688
+ z.z.object({
1689
+ type: z.z.literal("codex"),
1690
+ data: z.z.unknown()
1691
+ }),
1692
+ z.z.object({
1693
+ type: z.z.literal("acp"),
1694
+ provider: AgentProviderSchema,
1695
+ data: z.z.unknown()
1696
+ }),
1697
+ z.z.object({
1698
+ type: z.z.literal("event"),
1699
+ id: z.z.string().optional(),
1700
+ data: z.z.unknown()
1701
+ })
1702
+ ]),
1686
1703
  meta: MessageMetaSchema.optional()
1687
1704
  });
1688
1705
  const MessageContentSchema = z.z.union([UserMessageSchema, AgentMessageSchema]);
@@ -1702,6 +1719,59 @@ function isNeedsHandoffTask(task) {
1702
1719
  }
1703
1720
  return false;
1704
1721
  }
1722
+ function resolveRuntimeSessionId(task) {
1723
+ return task.specialistHomeSessionId ?? null;
1724
+ }
1725
+ function resolveMachineId(task) {
1726
+ return task.specialistHomeMachineId ?? null;
1727
+ }
1728
+ function resolveTaskMergeState(task) {
1729
+ const runtimeSessionId = resolveRuntimeSessionId(task);
1730
+ const machineId = resolveMachineId(task);
1731
+ if (task.accessChannelState === "runtime_replaced") {
1732
+ return "conflict";
1733
+ }
1734
+ if (task.activeOwnerAgentId && task.activeOwnerAgentId !== task.ownerAgentId) {
1735
+ return "conflict";
1736
+ }
1737
+ if (task.accessChannelState === "reattach_required") {
1738
+ return "merge_pending";
1739
+ }
1740
+ if (task.hasActiveOwner === false && task.status !== "done" && task.status !== "terminated") {
1741
+ return "merge_pending";
1742
+ }
1743
+ if ((task.status === "active" || task.status === "waiting_close") && (!runtimeSessionId || !machineId)) {
1744
+ return "merge_pending";
1745
+ }
1746
+ return "stable";
1747
+ }
1748
+ function resolveTaskMergeStateSummary(task, mergeState, runtimeSessionId, machineId) {
1749
+ if (mergeState === "conflict") {
1750
+ if (task.accessChannelState === "runtime_replaced") {
1751
+ return "runtime was replaced before the merge view stabilized";
1752
+ }
1753
+ if (task.activeOwnerAgentId && task.activeOwnerAgentId !== task.ownerAgentId) {
1754
+ return `active owner moved to ${task.activeOwnerAgentId}`;
1755
+ }
1756
+ return "merge conflict needs owner review";
1757
+ }
1758
+ if (mergeState === "merge_pending") {
1759
+ if (task.accessChannelState === "reattach_required") {
1760
+ return "runtime reattach required before merge becomes stable";
1761
+ }
1762
+ if (task.hasActiveOwner === false) {
1763
+ return "no active owner is attached to the task yet";
1764
+ }
1765
+ if (!runtimeSessionId || !machineId) {
1766
+ return "runtime or machine attribution is still missing";
1767
+ }
1768
+ return "merge visibility is still pending";
1769
+ }
1770
+ if (runtimeSessionId && machineId) {
1771
+ return `runtime ${runtimeSessionId} on machine ${machineId}`;
1772
+ }
1773
+ return "merge is stable";
1774
+ }
1705
1775
  function classifyHappyOrgTaskBoardSection(task) {
1706
1776
  if (task.status === "done") {
1707
1777
  return "closed";
@@ -1715,8 +1785,12 @@ function classifyHappyOrgTaskBoardSection(task) {
1715
1785
  return "in_progress";
1716
1786
  }
1717
1787
  function buildHappyOrgTaskBoardView(tasks) {
1788
+ const organizationManifestId = tasks.find((task) => task.organizationManifestId)?.organizationManifestId ?? null;
1718
1789
  const items = [...tasks].filter((task) => task.status !== "terminated").sort((left, right) => right.updatedAt - left.updatedAt || left.taskId.localeCompare(right.taskId)).map((task) => {
1719
1790
  const sectionKey = classifyHappyOrgTaskBoardSection(task);
1791
+ const runtimeSessionId = resolveRuntimeSessionId(task);
1792
+ const machineId = resolveMachineId(task);
1793
+ const mergeState = resolveTaskMergeState(task);
1720
1794
  return {
1721
1795
  taskId: task.taskId,
1722
1796
  title: task.title,
@@ -1725,8 +1799,15 @@ function buildHappyOrgTaskBoardView(tasks) {
1725
1799
  lane: sectionKey,
1726
1800
  ownerAgentId: task.ownerAgentId,
1727
1801
  ownerName: task.ownerName,
1802
+ organizationManifestId: task.organizationManifestId ?? null,
1803
+ positionId: task.positionId ?? null,
1728
1804
  responsibilityId: task.responsibilityId ?? null,
1729
1805
  responsibilityLabel: task.responsibilityLabel ?? null,
1806
+ runtimeSessionId,
1807
+ machineId,
1808
+ accessChannelState: task.accessChannelState ?? null,
1809
+ mergeState,
1810
+ mergeStateSummary: resolveTaskMergeStateSummary(task, mergeState, runtimeSessionId, machineId),
1730
1811
  currentProgress: task.positionStatus || task.summary || null,
1731
1812
  latestResult: task.latestUserVisibleResult || task.reviewSummary || task.resultSummary || null,
1732
1813
  blockerOrDecision: task.blockerSummary || task.blocker || task.decisionNeeded || task.adviceSummary || task.ceoWriteNextStep || null,
@@ -1750,6 +1831,7 @@ function buildHappyOrgTaskBoardView(tasks) {
1750
1831
  { key: "closed", count: buckets.closed.length, items: buckets.closed }
1751
1832
  ];
1752
1833
  return {
1834
+ organizationManifestId,
1753
1835
  totalCount: items.length,
1754
1836
  inProgressCount: sections[0].count,
1755
1837
  handoffCount: sections[1].count,
@@ -1760,6 +1842,49 @@ function buildHappyOrgTaskBoardView(tasks) {
1760
1842
  };
1761
1843
  }
1762
1844
 
1845
+ const HTTP_URL_PATTERN = /^https?:\/\//i;
1846
+ const FILE_URL_PATTERN = /^file:\/\//i;
1847
+ const FILE_EXTENSION_PATTERN = /\.[A-Za-z0-9_-]{1,16}$/;
1848
+ const SPECIAL_FILE_NAME_PATTERN = /(^|[\\/])(Dockerfile|Makefile|README(?:\.[A-Za-z0-9_-]+)?)$/i;
1849
+ function isLikelyPreviewableArtifactTarget(target) {
1850
+ const trimmed = typeof target === "string" ? target.trim() : "";
1851
+ if (!trimmed) {
1852
+ return false;
1853
+ }
1854
+ if (HTTP_URL_PATTERN.test(trimmed) || FILE_URL_PATTERN.test(trimmed)) {
1855
+ return true;
1856
+ }
1857
+ const stripped = trimmed.split(/[?#]/)[0] ?? trimmed;
1858
+ const basename = stripped.split(/[\\/]/).pop() ?? stripped;
1859
+ if (!basename) {
1860
+ return false;
1861
+ }
1862
+ return FILE_EXTENSION_PATTERN.test(basename) || SPECIAL_FILE_NAME_PATTERN.test(basename);
1863
+ }
1864
+ function resolveArtifactTarget(target) {
1865
+ const trimmed = typeof target === "string" ? target.trim() : "";
1866
+ if (!trimmed || !isLikelyPreviewableArtifactTarget(trimmed)) {
1867
+ return {
1868
+ kind: "unknown",
1869
+ target: trimmed
1870
+ };
1871
+ }
1872
+ if (HTTP_URL_PATTERN.test(trimmed) || FILE_URL_PATTERN.test(trimmed)) {
1873
+ return {
1874
+ kind: "external",
1875
+ target: trimmed
1876
+ };
1877
+ }
1878
+ return {
1879
+ kind: "path",
1880
+ target: trimmed
1881
+ };
1882
+ }
1883
+ function normalizePreviewableArtifactTarget(target) {
1884
+ const resolved = resolveArtifactTarget(target);
1885
+ return resolved.kind === "unknown" ? null : resolved.target;
1886
+ }
1887
+
1763
1888
  const HAPPY_ORG_LOCAL_REPO_SCHEMA_VERSION = "1.7.3-local-repo-v1";
1764
1889
  const HAPPY_ORG_WRITER_LOCK_SCHEMA_VERSION = "1.7.3-writer-lock-v1";
1765
1890
  const WRITER_LOCK_LEASE_DURATION_SECONDS = 900;
@@ -1801,6 +1926,12 @@ const GoalSchema = z.z.object({
1801
1926
  approvedBy: z.z.string().nullable(),
1802
1927
  updatedAt: z.z.number()
1803
1928
  });
1929
+ const OrganizationValueSchema = z.z.object({
1930
+ name: z.z.string(),
1931
+ slug: z.z.string().min(1),
1932
+ summary: z.z.string().nullable(),
1933
+ createdAt: z.z.number()
1934
+ });
1804
1935
  const TaskUpdatePayloadSchema = z.z.object({
1805
1936
  taskId: z.z.string().min(1),
1806
1937
  status: TaskStatusSchema,
@@ -2028,6 +2159,7 @@ const LogValueSchema = z.z.object({
2028
2159
  taskId: z.z.string(),
2029
2160
  entries: z.z.array(LogEntrySchema)
2030
2161
  });
2162
+ const OrganizationEnvelopeSchema = RevisionEnvelopeSchema(OrganizationValueSchema);
2031
2163
  const GoalEnvelopeSchema = RevisionEnvelopeSchema(GoalSchema);
2032
2164
  const PositionEnvelopeSchema = RevisionEnvelopeSchema(PositionValueSchema);
2033
2165
  const WorkspaceEnvelopeSchema = RevisionEnvelopeSchema(WorkspaceValueSchema);
@@ -2324,20 +2456,31 @@ async function readHappyOrgDispatchTruthSnapshot(rootPath) {
2324
2456
  };
2325
2457
  }
2326
2458
  async function readHappyOrgRepoTaskBoard(rootPath) {
2327
- const [taskIds, positions, responsibilities] = await Promise.all([
2459
+ const [taskIds, positions, responsibilities, organization] = await Promise.all([
2328
2460
  listTaskIds(rootPath),
2329
2461
  listPositionEnvelopes(rootPath),
2330
- readResponsibilities(rootPath)
2462
+ readResponsibilities(rootPath),
2463
+ readEnvelope(path.join(rootPath, "ORGANIZATION.json"), OrganizationEnvelopeSchema)
2331
2464
  ]);
2465
+ const organizationManifestId = organization?.value.slug ?? null;
2332
2466
  const responsibilityLabelById = /* @__PURE__ */ new Map();
2467
+ const responsibilityPositionIdById = /* @__PURE__ */ new Map();
2468
+ const positionIdByAgentId = /* @__PURE__ */ new Map();
2333
2469
  for (const responsibility of responsibilities?.value ?? []) {
2334
2470
  responsibilityLabelById.set(responsibility.responsibilityId, responsibility.title);
2471
+ responsibilityPositionIdById.set(responsibility.responsibilityId, responsibility.positionId);
2335
2472
  }
2336
2473
  for (const position of positions) {
2337
2474
  const fallbackResponsibilityId = position.value.responsibilityIds[0] ?? buildResponsibilityId(position.value.slug);
2338
2475
  if (!responsibilityLabelById.has(fallbackResponsibilityId)) {
2339
2476
  responsibilityLabelById.set(fallbackResponsibilityId, position.value.label || position.value.agentName || position.value.slug);
2340
2477
  }
2478
+ if (!responsibilityPositionIdById.has(fallbackResponsibilityId)) {
2479
+ responsibilityPositionIdById.set(fallbackResponsibilityId, position.value.positionId);
2480
+ }
2481
+ if (position.value.agentId) {
2482
+ positionIdByAgentId.set(position.value.agentId, position.value.positionId);
2483
+ }
2341
2484
  }
2342
2485
  const tasks = await Promise.all(taskIds.map(async (taskId) => {
2343
2486
  const bundle = await loadTaskEnvelope(rootPath, taskId);
@@ -2345,6 +2488,8 @@ async function readHappyOrgRepoTaskBoard(rootPath) {
2345
2488
  }));
2346
2489
  return buildHappyOrgTaskBoardView(tasks.filter((task) => task !== null).map((task) => ({
2347
2490
  ...task,
2491
+ organizationManifestId,
2492
+ positionId: (task.responsibilityId ? responsibilityPositionIdById.get(task.responsibilityId) ?? null : null) ?? positionIdByAgentId.get(task.activeOwnerAgentId ?? "") ?? positionIdByAgentId.get(task.ownerAgentId) ?? null,
2348
2493
  responsibilityLabel: task.responsibilityLabel ?? (task.responsibilityId ? responsibilityLabelById.get(task.responsibilityId) ?? null : null)
2349
2494
  })));
2350
2495
  }
@@ -3037,6 +3182,10 @@ async function recordHappyOrgTurnReport(options) {
3037
3182
  }
3038
3183
  const now = Date.now();
3039
3184
  const nextStatus = nextStatusFromTurnReport(options.report);
3185
+ const artifactReference = normalizePreviewableArtifactTarget(options.report.targetArtifact);
3186
+ const artifactTarget = resolveArtifactTarget(options.report.targetArtifact);
3187
+ const nextPath = artifactTarget.kind === "path" ? artifactTarget.target : bundle.task.value.path;
3188
+ const nextPortablePath = artifactTarget.kind === "path" ? normalizePortablePath(artifactTarget.target) : bundle.task.value.portablePath;
3040
3189
  const nextTaskValue = {
3041
3190
  ...bundle.task.value,
3042
3191
  status: nextStatus,
@@ -3064,8 +3213,8 @@ async function recordHappyOrgTurnReport(options) {
3064
3213
  blockerSummary: options.report.acceptanceHandoff?.blockerSummary ?? bundle.task.value.blockerSummary ?? null,
3065
3214
  acceptanceState: options.report.acceptanceHandoff?.acceptanceState ?? bundle.task.value.acceptanceState ?? null,
3066
3215
  ceoWriteNextStep: options.report.acceptanceHandoff?.ceoWriteNextStep ?? bundle.task.value.ceoWriteNextStep ?? null,
3067
- path: options.report.targetArtifact ?? bundle.task.value.path,
3068
- portablePath: normalizePortablePath(options.report.targetArtifact ?? bundle.task.value.path)
3216
+ path: nextPath,
3217
+ portablePath: nextPortablePath
3069
3218
  };
3070
3219
  const taskRoot = buildTaskRoot(options.rootPath, options.report.taskId);
3071
3220
  await writeEnvelope(path.join(taskRoot, "TASK.json"), {
@@ -3104,7 +3253,7 @@ async function recordHappyOrgTurnReport(options) {
3104
3253
  reviewSummary: bundle.result?.value.reviewSummary ?? null,
3105
3254
  assetCaptureStatus: bundle.result?.value.assetCaptureStatus ?? null,
3106
3255
  assetCaptureSummary: bundle.result?.value.assetCaptureSummary ?? null,
3107
- assetReference: options.report.targetArtifact ?? bundle.result?.value.assetReference ?? null,
3256
+ assetReference: artifactReference ?? bundle.result?.value.assetReference ?? null,
3108
3257
  closedAt: nextStatus === "waiting_close" ? now : bundle.result?.value.closedAt ?? null,
3109
3258
  updatedAt: now,
3110
3259
  updatedBy: options.report.memberAgentId
@@ -3535,6 +3684,12 @@ async function runShellCommand(opts) {
3535
3684
  });
3536
3685
  }
3537
3686
 
3687
+ function quoteShellArgument(value) {
3688
+ if (value.length === 0) {
3689
+ return "''";
3690
+ }
3691
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
3692
+ }
3538
3693
  function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
3539
3694
  rpcHandlerManager.registerHandler("bash", async (data) => {
3540
3695
  logger.debug("Shell command request:", data.command);
@@ -3580,6 +3735,52 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
3580
3735
  return result;
3581
3736
  }
3582
3737
  });
3738
+ rpcHandlerManager.registerHandler("ripgrep", async (data) => {
3739
+ logger.debug("Ripgrep request:", data.args);
3740
+ if (data.cwd) {
3741
+ const validation = validatePath(data.cwd, workingDirectory);
3742
+ if (!validation.valid) {
3743
+ return { success: false, error: validation.error };
3744
+ }
3745
+ }
3746
+ try {
3747
+ const cwd = data.cwd || workingDirectory;
3748
+ const command = ["rg", ...data.args.map(quoteShellArgument)].join(" ");
3749
+ logger.debug("Ripgrep executing...", {
3750
+ cwd,
3751
+ timeout: data.timeout || 3e4,
3752
+ args: data.args
3753
+ });
3754
+ const result = await runShellCommand({
3755
+ command,
3756
+ cwd,
3757
+ timeoutMs: data.timeout || 3e4
3758
+ });
3759
+ logger.debug("Ripgrep finished:", {
3760
+ success: result.success,
3761
+ exitCode: result.exitCode,
3762
+ error: result.error,
3763
+ stdoutLen: result.stdout.length,
3764
+ stderrLen: result.stderr.length
3765
+ });
3766
+ return result;
3767
+ } catch (error) {
3768
+ const result = {
3769
+ success: false,
3770
+ stdout: "",
3771
+ stderr: "",
3772
+ exitCode: 1,
3773
+ error: error instanceof Error ? error.message : "Ripgrep failed"
3774
+ };
3775
+ logger.debug("Ripgrep failed before execution completed:", {
3776
+ cwd: data.cwd || workingDirectory,
3777
+ success: false,
3778
+ exitCode: result.exitCode,
3779
+ error: result.error
3780
+ });
3781
+ return result;
3782
+ }
3783
+ });
3583
3784
  rpcHandlerManager.registerHandler("readFile", async (data) => {
3584
3785
  logger.debug("Read file request:", data.path);
3585
3786
  const validation = validatePath(data.path, workingDirectory);
@@ -4142,9 +4343,95 @@ function isTransientMessageStreamDelta(body) {
4142
4343
  return record.type === "message" && record.phase === "delta";
4143
4344
  }
4144
4345
 
4346
+ function deterministicStringify(obj, options = {}) {
4347
+ const {
4348
+ undefinedBehavior = "omit",
4349
+ sortArrays = false,
4350
+ replacer,
4351
+ includeSymbols = false
4352
+ } = options;
4353
+ const seen = /* @__PURE__ */ new WeakSet();
4354
+ function processValue(value, key) {
4355
+ if (replacer && key !== void 0) {
4356
+ value = replacer(key, value);
4357
+ }
4358
+ if (value === null) return null;
4359
+ if (value === void 0) {
4360
+ switch (undefinedBehavior) {
4361
+ case "omit":
4362
+ return void 0;
4363
+ case "null":
4364
+ return null;
4365
+ case "throw":
4366
+ throw new Error(`Undefined value at key: ${key}`);
4367
+ }
4368
+ }
4369
+ if (typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
4370
+ return value;
4371
+ }
4372
+ if (value instanceof Date) {
4373
+ return value.toISOString();
4374
+ }
4375
+ if (value instanceof RegExp) {
4376
+ return value.toString();
4377
+ }
4378
+ if (typeof value === "function") {
4379
+ return void 0;
4380
+ }
4381
+ if (typeof value === "symbol") {
4382
+ return includeSymbols ? value.toString() : void 0;
4383
+ }
4384
+ if (typeof value === "bigint") {
4385
+ return value.toString() + "n";
4386
+ }
4387
+ if (seen.has(value)) {
4388
+ throw new Error("Circular reference detected");
4389
+ }
4390
+ seen.add(value);
4391
+ if (Array.isArray(value)) {
4392
+ const processed2 = value.map((item, index) => processValue(item, String(index))).filter((item) => item !== void 0);
4393
+ if (sortArrays) {
4394
+ processed2.sort((a, b) => {
4395
+ const aStr = JSON.stringify(processValue(a));
4396
+ const bStr = JSON.stringify(processValue(b));
4397
+ return aStr.localeCompare(bStr);
4398
+ });
4399
+ }
4400
+ seen.delete(value);
4401
+ return processed2;
4402
+ }
4403
+ if (value.constructor === Object || value.constructor === void 0) {
4404
+ const processed2 = {};
4405
+ const keys = Object.keys(value).sort();
4406
+ for (const k of keys) {
4407
+ const processedValue = processValue(value[k], k);
4408
+ if (processedValue !== void 0) {
4409
+ processed2[k] = processedValue;
4410
+ }
4411
+ }
4412
+ seen.delete(value);
4413
+ return processed2;
4414
+ }
4415
+ try {
4416
+ const plain = { ...value };
4417
+ seen.delete(value);
4418
+ return processValue(plain, key);
4419
+ } catch {
4420
+ seen.delete(value);
4421
+ return String(value);
4422
+ }
4423
+ }
4424
+ const processed = processValue(obj);
4425
+ return JSON.stringify(processed);
4426
+ }
4427
+ function hashObject(obj, options, encoding = "hex") {
4428
+ const jsonString = deterministicStringify(obj, options);
4429
+ return crypto.createHash("sha256").update(jsonString).digest(encoding);
4430
+ }
4431
+
4145
4432
  const MAX_PENDING_RELIABLE_SESSION_MESSAGES = 200;
4146
4433
  const MAX_PENDING_RELIABLE_SESSION_MESSAGE_BYTES = 512 * 1024;
4147
- const PROTOCOL_V3_INITIAL_SNAPSHOT_LIMIT = 150;
4434
+ const PROTOCOL_V3_INITIAL_SNAPSHOT_LIMIT = 300;
4148
4435
  const PROTOCOL_V3_CHANGES_PAGE_LIMIT = 200;
4149
4436
  const PROTOCOL_V3_CAPABILITIES_WAIT_MS = 250;
4150
4437
  const COMMITTED_SESSION_WRITE_ACK_TIMEOUT_MS = 5e3;
@@ -4277,8 +4564,11 @@ class ApiSessionClient extends node_events.EventEmitter {
4277
4564
  agentState;
4278
4565
  agentStateVersion;
4279
4566
  socket;
4567
+ initialSessionSeq;
4280
4568
  pendingMessages = [];
4281
4569
  pendingMessageCallback = null;
4570
+ pendingSessionMessages = [];
4571
+ pendingSessionMessageCallback = null;
4282
4572
  rpcHandlerManager;
4283
4573
  agentStateLock = new AsyncLock();
4284
4574
  metadataLock = new AsyncLock();
@@ -4318,6 +4608,7 @@ class ApiSessionClient extends node_events.EventEmitter {
4318
4608
  this.agentStateVersion = session.agentStateVersion;
4319
4609
  this.encryptionKey = session.encryptionKey;
4320
4610
  this.encryptionVariant = session.encryptionVariant;
4611
+ this.initialSessionSeq = session.seq;
4321
4612
  this.lastChangeSeq = session.seq;
4322
4613
  this.committedSessionWriteAckMode = this.credentials.signing ? "unknown" : "unsupported";
4323
4614
  for (const [dispatchId, ack] of Object.entries(session.metadata?.happyOrg?.dispatchAcks ?? {})) {
@@ -4432,6 +4723,12 @@ class ApiSessionClient extends node_events.EventEmitter {
4432
4723
  callback(this.pendingMessages.shift());
4433
4724
  }
4434
4725
  }
4726
+ onSessionMessage(callback) {
4727
+ this.pendingSessionMessageCallback = callback;
4728
+ while (this.pendingSessionMessages.length > 0) {
4729
+ callback(this.pendingSessionMessages.shift());
4730
+ }
4731
+ }
4435
4732
  configureProtocolV3Sync(options) {
4436
4733
  this.invalidateProtocolV3SessionSync();
4437
4734
  this.protocolV3SessionSync = options;
@@ -4458,6 +4755,13 @@ class ApiSessionClient extends node_events.EventEmitter {
4458
4755
  }
4459
4756
  return preserveSessionRuntimeMetadata(this.metadata, next);
4460
4757
  }
4758
+ didSessionValueChange(current, next) {
4759
+ try {
4760
+ return deterministicStringify(current ?? null) !== deterministicStringify(next ?? null);
4761
+ } catch {
4762
+ return true;
4763
+ }
4764
+ }
4461
4765
  async waitForMetadataUpdate(signal) {
4462
4766
  if (signal?.aborted) {
4463
4767
  throw createAbortError();
@@ -4710,7 +5014,10 @@ class ApiSessionClient extends node_events.EventEmitter {
4710
5014
  updateMetadata(handler) {
4711
5015
  this.trackPendingBackgroundTask(this.metadataLock.inLock(async () => {
4712
5016
  await backoff(async () => {
4713
- let updated = this.mergeRuntimeMetadata(handler(this.metadata));
5017
+ const updated = this.mergeRuntimeMetadata(handler(this.metadata));
5018
+ if (!this.didSessionValueChange(this.metadata, updated)) {
5019
+ return;
5020
+ }
4714
5021
  const sessionIndex = buildSessionRuntimeIndex(updated);
4715
5022
  const answer = await this.socket.emitWithAck("update-metadata", {
4716
5023
  sid: this.sessionId,
@@ -4756,7 +5063,10 @@ class ApiSessionClient extends node_events.EventEmitter {
4756
5063
  logger.debugLargeJson("Updating agent state", this.agentState);
4757
5064
  this.trackPendingBackgroundTask(this.agentStateLock.inLock(async () => {
4758
5065
  await backoff(async () => {
4759
- let updated = handler(this.agentState || {});
5066
+ const updated = handler(this.agentState || {});
5067
+ if (!this.didSessionValueChange(this.agentState, updated)) {
5068
+ return;
5069
+ }
4760
5070
  const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) : null });
4761
5071
  if (answer.result === "success") {
4762
5072
  this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
@@ -4838,7 +5148,10 @@ class ApiSessionClient extends node_events.EventEmitter {
4838
5148
  if (messageSeq !== null && messageSeq <= this.lastChangeSeq) {
4839
5149
  return;
4840
5150
  }
4841
- this.dispatchPlaintextSessionMessage(data.body.message.content.p);
5151
+ this.dispatchPlaintextSessionMessage(data.body.message.content.p, {
5152
+ source: "live",
5153
+ seq: messageSeq
5154
+ });
4842
5155
  if (messageSeq !== null) {
4843
5156
  this.lastChangeSeq = Math.max(this.lastChangeSeq, messageSeq);
4844
5157
  }
@@ -4849,7 +5162,10 @@ class ApiSessionClient extends node_events.EventEmitter {
4849
5162
  if (messageSeq !== null && messageSeq <= this.lastChangeSeq) {
4850
5163
  return;
4851
5164
  }
4852
- this.dispatchEncryptedSessionMessage(data.body.message.content.c);
5165
+ this.dispatchEncryptedSessionMessage(data.body.message.content.c, {
5166
+ source: "live",
5167
+ seq: messageSeq
5168
+ });
4853
5169
  if (messageSeq !== null) {
4854
5170
  this.lastChangeSeq = Math.max(this.lastChangeSeq, messageSeq);
4855
5171
  }
@@ -4894,18 +5210,25 @@ class ApiSessionClient extends node_events.EventEmitter {
4894
5210
  }
4895
5211
  this.emit("message", data.body);
4896
5212
  }
4897
- dispatchEncryptedSessionMessage(encryptedMessage) {
5213
+ dispatchEncryptedSessionMessage(encryptedMessage, options) {
4898
5214
  const body = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(encryptedMessage));
4899
5215
  logger.debugLargeJson("[SOCKET] [UPDATE] Received update:", body);
4900
5216
  const parsedBody = body ?? buildHappyOrgFallbackUserMessage(encryptedMessage);
4901
- const userResult = UserMessageSchema.safeParse(parsedBody);
4902
- if (userResult.success) {
4903
- this.maybeAutoAcknowledgeHappyOrgDispatch(userResult.data);
4904
- if (this.pendingMessageCallback) {
4905
- this.pendingMessageCallback(userResult.data);
4906
- } else {
4907
- this.pendingMessages.push(userResult.data);
5217
+ const messageResult = MessageContentSchema.safeParse(parsedBody);
5218
+ if (messageResult.success) {
5219
+ const queuesPendingInput = this.dispatchSessionTranscriptMessage(messageResult.data, options);
5220
+ if (messageResult.data.role === "user") {
5221
+ if (queuesPendingInput) {
5222
+ this.maybeAutoAcknowledgeHappyOrgDispatch(messageResult.data);
5223
+ if (this.pendingMessageCallback) {
5224
+ this.pendingMessageCallback(messageResult.data);
5225
+ } else {
5226
+ this.pendingMessages.push(messageResult.data);
5227
+ }
5228
+ }
5229
+ return;
4908
5230
  }
5231
+ this.emit("message", messageResult.data);
4909
5232
  return;
4910
5233
  }
4911
5234
  this.emit("message", parsedBody);
@@ -4914,7 +5237,7 @@ class ApiSessionClient extends node_events.EventEmitter {
4914
5237
  * Dispatch a plaintext session message (AES mode: server already decrypted).
4915
5238
  * The plaintext JSON is parsed and validated as a UserMessage.
4916
5239
  */
4917
- dispatchPlaintextSessionMessage(plaintextJson) {
5240
+ dispatchPlaintextSessionMessage(plaintextJson, options) {
4918
5241
  logger.debug("[SOCKET] [UPDATE] [PLAINTEXT] Received plaintext message");
4919
5242
  let parsedBody = null;
4920
5243
  try {
@@ -4923,19 +5246,41 @@ class ApiSessionClient extends node_events.EventEmitter {
4923
5246
  logger.debug("[SOCKET] [UPDATE] [PLAINTEXT] Failed to parse plaintext JSON, ignoring");
4924
5247
  return;
4925
5248
  }
4926
- const userResult = UserMessageSchema.safeParse(parsedBody);
4927
- if (userResult.success) {
4928
- this.maybeAutoAcknowledgeHappyOrgDispatch(userResult.data);
4929
- if (this.pendingMessageCallback) {
4930
- this.pendingMessageCallback(userResult.data);
4931
- } else {
4932
- this.pendingMessages.push(userResult.data);
5249
+ const messageResult = MessageContentSchema.safeParse(parsedBody);
5250
+ if (messageResult.success) {
5251
+ const queuesPendingInput = this.dispatchSessionTranscriptMessage(messageResult.data, options);
5252
+ if (messageResult.data.role === "user") {
5253
+ if (queuesPendingInput) {
5254
+ this.maybeAutoAcknowledgeHappyOrgDispatch(messageResult.data);
5255
+ if (this.pendingMessageCallback) {
5256
+ this.pendingMessageCallback(messageResult.data);
5257
+ } else {
5258
+ this.pendingMessages.push(messageResult.data);
5259
+ }
5260
+ }
5261
+ return;
4933
5262
  }
5263
+ this.emit("message", messageResult.data);
4934
5264
  return;
4935
5265
  }
4936
- logger.debug("[SOCKET] [UPDATE] [PLAINTEXT] Message did not match UserMessageSchema, emitting generic event");
5266
+ logger.debug("[SOCKET] [UPDATE] [PLAINTEXT] Message did not match MessageContentSchema, emitting generic event");
4937
5267
  this.emit("message", parsedBody);
4938
5268
  }
5269
+ dispatchSessionTranscriptMessage(message, options) {
5270
+ const queuesPendingInput = message.role === "user" && (options.source === "live" || this.initialSessionSeq === 0);
5271
+ const transcriptMessage = {
5272
+ source: options.source,
5273
+ seq: typeof options.seq === "number" ? options.seq : null,
5274
+ message,
5275
+ queuesPendingInput
5276
+ };
5277
+ if (this.pendingSessionMessageCallback) {
5278
+ this.pendingSessionMessageCallback(transcriptMessage);
5279
+ } else {
5280
+ this.pendingSessionMessages.push(transcriptMessage);
5281
+ }
5282
+ return queuesPendingInput;
5283
+ }
4939
5284
  maybeAutoAcknowledgeHappyOrgDispatch(message) {
4940
5285
  const candidate = this.resolveHappyOrgDispatchBusinessAckCandidate(message);
4941
5286
  if (!candidate) {
@@ -5240,7 +5585,7 @@ class ApiSessionClient extends node_events.EventEmitter {
5240
5585
  sessionId: this.sessionId,
5241
5586
  lastChangeSeq: this.lastChangeSeq
5242
5587
  });
5243
- if (!this.initialProtocolV3SnapshotComplete && this.lastChangeSeq === 0 && (!this.protocolV3Descriptor || this.supportsKnownProtocolCapability("session_snapshot_v3"))) {
5588
+ if (!this.initialProtocolV3SnapshotComplete && (!this.protocolV3Descriptor || this.supportsKnownProtocolCapability("session_snapshot_v3"))) {
5244
5589
  const snapshot = await this.protocolV3SessionSync.getSessionSnapshot(
5245
5590
  this.sessionId,
5246
5591
  this.protocolV3SessionSync.snapshotLimit ?? PROTOCOL_V3_INITIAL_SNAPSHOT_LIMIT
@@ -5297,13 +5642,16 @@ class ApiSessionClient extends node_events.EventEmitter {
5297
5642
  applyProtocolV3Snapshot(snapshot) {
5298
5643
  this.applyProtocolV3SessionOverlay(snapshot.session);
5299
5644
  for (const message of snapshot.snapshot.messages) {
5300
- if (message.seq <= this.lastChangeSeq) {
5301
- continue;
5302
- }
5303
5645
  if (message.content.t === "plaintext") {
5304
- this.dispatchPlaintextSessionMessage(message.content.p);
5646
+ this.dispatchPlaintextSessionMessage(message.content.p, {
5647
+ source: "snapshot",
5648
+ seq: message.seq
5649
+ });
5305
5650
  } else if (message.content.t === "encrypted") {
5306
- this.dispatchEncryptedSessionMessage(message.content.c);
5651
+ this.dispatchEncryptedSessionMessage(message.content.c, {
5652
+ source: "snapshot",
5653
+ seq: message.seq
5654
+ });
5307
5655
  }
5308
5656
  this.lastChangeSeq = Math.max(this.lastChangeSeq, message.seq);
5309
5657
  }
@@ -5332,11 +5680,18 @@ class ApiSessionClient extends node_events.EventEmitter {
5332
5680
  case "message.created": {
5333
5681
  const message = payload.message;
5334
5682
  if (message && typeof message === "object") {
5683
+ const messageSeq = typeof message.seq === "number" ? message.seq : null;
5335
5684
  const content = message.content;
5336
5685
  if (content && typeof content === "object" && content.t === "plaintext" && typeof content.p === "string") {
5337
- this.dispatchPlaintextSessionMessage(content.p);
5686
+ this.dispatchPlaintextSessionMessage(content.p, {
5687
+ source: "live",
5688
+ seq: messageSeq ?? change.changeSeq
5689
+ });
5338
5690
  } else if (content && typeof content === "object" && content.t === "encrypted" && typeof content.c === "string") {
5339
- this.dispatchEncryptedSessionMessage(content.c);
5691
+ this.dispatchEncryptedSessionMessage(content.c, {
5692
+ source: "live",
5693
+ seq: messageSeq ?? change.changeSeq
5694
+ });
5340
5695
  }
5341
5696
  }
5342
5697
  break;
@@ -5757,12 +6112,12 @@ class ApiMachineClient {
5757
6112
  requestShutdown
5758
6113
  }) {
5759
6114
  this.rpcHandlerManager.registerHandler("spawn-happy-session", async (params) => {
5760
- const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token, environmentVariables } = params || {};
6115
+ const { directory, sessionId, machineId, managedSessionTag, approvedNewDirectoryCreation, agent, token, resume, environmentVariables } = params || {};
5761
6116
  logger.debug(`[API MACHINE] Spawning session with params: ${JSON.stringify(params)}`);
5762
6117
  if (!directory) {
5763
6118
  throw new Error("Directory is required");
5764
6119
  }
5765
- const result = await spawnSession({ directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token, environmentVariables });
6120
+ const result = await spawnSession({ directory, sessionId, machineId, managedSessionTag, approvedNewDirectoryCreation, agent, token, resume, environmentVariables });
5766
6121
  switch (result.type) {
5767
6122
  case "success":
5768
6123
  logger.debug(`[API MACHINE] Spawned session ${result.sessionId}`);
@@ -6968,8 +7323,10 @@ exports.encrypt = encrypt;
6968
7323
  exports.ensureSigningCredentials = ensureSigningCredentials;
6969
7324
  exports.getLatestDaemonLog = getLatestDaemonLog;
6970
7325
  exports.getProfileEnvironmentVariables = getProfileEnvironmentVariables;
7326
+ exports.hashObject = hashObject;
6971
7327
  exports.isAuthenticationRequiredError = isAuthenticationRequiredError;
6972
7328
  exports.logger = logger;
7329
+ exports.normalizePreviewableArtifactTarget = normalizePreviewableArtifactTarget;
6973
7330
  exports.packageJson = packageJson;
6974
7331
  exports.persistence = persistence;
6975
7332
  exports.preserveSessionRuntimeMetadata = preserveSessionRuntimeMetadata;