@tutti-os/agent-gui 0.0.46 → 0.0.47

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 (60) hide show
  1. package/dist/{AgentMentionSearchController-D5ks45fN.d.ts → AgentMentionSearchController-CExFPobz.d.ts} +1 -1
  2. package/dist/AgentMessageMarkdown-DeYPURtF.d.ts +7 -0
  3. package/dist/agent-conversation/index.d.ts +4 -3
  4. package/dist/agent-conversation/index.js +8 -8
  5. package/dist/agent-message-center/index.d.ts +1 -1
  6. package/dist/agent-message-center/index.js +9 -9
  7. package/dist/{agentConversationVM-1QgRwvwZ.d.ts → agentConversationVM-BTMtRteS.d.ts} +14 -0
  8. package/dist/{AgentMessageMarkdown-Cts0dAIm.d.ts → agentGuiNodeTypes-B5Y6yDKy.d.ts} +43 -9
  9. package/dist/app/renderer/agentactivity.css +92 -9
  10. package/dist/{chunk-FRG36S6N.js → chunk-2FOBSURO.js} +5 -5
  11. package/dist/chunk-2FOBSURO.js.map +1 -0
  12. package/dist/{chunk-AMNXH4DJ.js → chunk-2RYZ4EBF.js} +2 -2
  13. package/dist/{chunk-EIC7XS3C.js → chunk-5QTC2L52.js} +2 -2
  14. package/dist/{chunk-ZJ7OSY4T.js → chunk-7NCWSH7U.js} +9 -9
  15. package/dist/{chunk-ZJ7OSY4T.js.map → chunk-7NCWSH7U.js.map} +1 -1
  16. package/dist/{chunk-62QOR3TC.js → chunk-HCS3HUUX.js} +5 -5
  17. package/dist/{chunk-OL54R6OL.js → chunk-J3SJZMI5.js} +2 -2
  18. package/dist/{chunk-VFQR7AZI.js → chunk-KACBTC7Y.js} +25 -3
  19. package/dist/chunk-KACBTC7Y.js.map +1 -0
  20. package/dist/{chunk-FJG2EH4V.js → chunk-KUCWRSXU.js} +2 -2
  21. package/dist/{chunk-D6IYEVDT.js → chunk-MCH35MAX.js} +2 -2
  22. package/dist/{chunk-NZ2BGOJU.js → chunk-Q4X6Q4E6.js} +2 -2
  23. package/dist/{chunk-EU7RWKHE.js → chunk-R6GOICRO.js} +5 -5
  24. package/dist/chunk-SD56WDSC.js +359 -0
  25. package/dist/chunk-SD56WDSC.js.map +1 -0
  26. package/dist/{chunk-TXXWUG2J.js → chunk-VE6JY2TH.js} +2 -1
  27. package/dist/{chunk-TXXWUG2J.js.map → chunk-VE6JY2TH.js.map} +1 -1
  28. package/dist/{chunk-QT45WUPQ.js → chunk-XJXSR5XI.js} +244 -21
  29. package/dist/chunk-XJXSR5XI.js.map +1 -0
  30. package/dist/{chunk-BAQTM6VS.js → chunk-ZAF4IVUT.js} +848 -102
  31. package/dist/chunk-ZAF4IVUT.js.map +1 -0
  32. package/dist/context-mention-palette/index.d.ts +3 -3
  33. package/dist/context-mention-palette/index.js +9 -9
  34. package/dist/i18n/index.d.ts +24 -2
  35. package/dist/i18n/index.js +2 -2
  36. package/dist/index.d.ts +22 -6
  37. package/dist/index.js +847 -812
  38. package/dist/index.js.map +1 -1
  39. package/dist/plan-decision-ops.d.ts +1 -1
  40. package/dist/queued-prompt-runtime.d.ts +97 -0
  41. package/dist/queued-prompt-runtime.js +9 -0
  42. package/dist/queued-prompt-runtime.js.map +1 -0
  43. package/dist/workbench/contribution.js +3 -3
  44. package/dist/workbench/index.js +9 -9
  45. package/dist/workbench/launch.js +2 -2
  46. package/dist/workbench/sessionTitle.js +3 -3
  47. package/dist/workspace-agent-generated-files.js +3 -3
  48. package/package.json +21 -14
  49. package/dist/chunk-BAQTM6VS.js.map +0 -1
  50. package/dist/chunk-FRG36S6N.js.map +0 -1
  51. package/dist/chunk-QT45WUPQ.js.map +0 -1
  52. package/dist/chunk-VFQR7AZI.js.map +0 -1
  53. /package/dist/{chunk-AMNXH4DJ.js.map → chunk-2RYZ4EBF.js.map} +0 -0
  54. /package/dist/{chunk-EIC7XS3C.js.map → chunk-5QTC2L52.js.map} +0 -0
  55. /package/dist/{chunk-62QOR3TC.js.map → chunk-HCS3HUUX.js.map} +0 -0
  56. /package/dist/{chunk-OL54R6OL.js.map → chunk-J3SJZMI5.js.map} +0 -0
  57. /package/dist/{chunk-FJG2EH4V.js.map → chunk-KUCWRSXU.js.map} +0 -0
  58. /package/dist/{chunk-D6IYEVDT.js.map → chunk-MCH35MAX.js.map} +0 -0
  59. /package/dist/{chunk-NZ2BGOJU.js.map → chunk-Q4X6Q4E6.js.map} +0 -0
  60. /package/dist/{chunk-EU7RWKHE.js.map → chunk-R6GOICRO.js.map} +0 -0
@@ -10,7 +10,7 @@ import {
10
10
  resolveWorkspaceFileLinkAction,
11
11
  resolveWorkspaceFilePathCandidate,
12
12
  resolveWorkspaceLinkAction
13
- } from "./chunk-QT45WUPQ.js";
13
+ } from "./chunk-XJXSR5XI.js";
14
14
  import {
15
15
  AGENT_RICH_TEXT_CARET_ANCHOR,
16
16
  attrsToMentionItem,
@@ -18,7 +18,7 @@ import {
18
18
  formatAgentMentionMarkdown,
19
19
  mentionItemToAttrs,
20
20
  parseAgentMentionMarkdown
21
- } from "./chunk-FRG36S6N.js";
21
+ } from "./chunk-2FOBSURO.js";
22
22
  import {
23
23
  fileChangeCountFromChanges,
24
24
  fileChangeEntriesFromChanges,
@@ -28,13 +28,14 @@ import {
28
28
  import {
29
29
  getOptionalAgentActivityRuntime,
30
30
  useOptionalAgentHostApi
31
- } from "./chunk-TXXWUG2J.js";
31
+ } from "./chunk-VE6JY2TH.js";
32
32
  import {
33
33
  workspaceAgentProviderLabel
34
34
  } from "./chunk-TYGL25EL.js";
35
35
  import {
36
+ getActiveUiLanguage,
36
37
  translate
37
- } from "./chunk-EIC7XS3C.js";
38
+ } from "./chunk-5QTC2L52.js";
38
39
 
39
40
  // shared/imageGenerationTool.ts
40
41
  var KNOWN_IMAGE_GENERATION_TOOL_NAMES = /* @__PURE__ */ new Set([
@@ -3350,7 +3351,10 @@ function tryExtractContentField(text) {
3350
3351
 
3351
3352
  // shared/agentConversation/projection/agentTurnSummaryProjection.ts
3352
3353
  function projectAgentTurnSummaryRows(detail) {
3353
- const options = { workspaceRoot: detail.workspaceRoot };
3354
+ const options = {
3355
+ defaultCwd: detail.cwd,
3356
+ workspaceRoot: detail.workspaceRoot
3357
+ };
3354
3358
  const rows = detail.turns.flatMap(
3355
3359
  (turn) => projectAgentTurnSummaryRowForTurn(turn, options)
3356
3360
  );
@@ -3402,16 +3406,14 @@ function normalizedActivityFilePath(value, options) {
3402
3406
  }) ? path : null;
3403
3407
  }
3404
3408
  function projectAgentTurnSummaryRowForTurn(turn, options = {}) {
3409
+ const summaryCalls = turnToolCallsForSummary(turn);
3405
3410
  const files = applyShortestUniqueFileLabels(
3406
- dedupeFiles(
3407
- turnToolCallsForSummary(turn).flatMap(
3408
- (call) => filesFromCall(call, options)
3409
- )
3410
- )
3411
+ dedupeFiles(summaryCalls.flatMap((call) => filesFromCall(call, options)))
3411
3412
  );
3412
3413
  if (files.length === 0) {
3413
3414
  return [];
3414
3415
  }
3416
+ const patchBatches = patchBatchesFromCalls(summaryCalls, options);
3415
3417
  const fileCount = files.length;
3416
3418
  const createdCount = files.filter(
3417
3419
  (file) => file.changeType === "created"
@@ -3424,6 +3426,7 @@ function projectAgentTurnSummaryRowForTurn(turn, options = {}) {
3424
3426
  id: `turn-summary:${turn.id}`,
3425
3427
  turnId: turn.id,
3426
3428
  files,
3429
+ ...patchBatches.length > 0 ? { patchBatches } : {},
3427
3430
  fileCount,
3428
3431
  modifiedCount,
3429
3432
  createdCount,
@@ -3453,11 +3456,10 @@ function turnToolCallsForSummary(turn) {
3453
3456
  }
3454
3457
  function filesFromCall(call, options) {
3455
3458
  const toolState = objectValue5(call.payload?.tool_state);
3456
- const metadata = objectValue5(call.payload?.metadata);
3457
3459
  const input = objectValue5(call.payload?.input) ?? objectValue5(toolState?.input) ?? summaryPathInput(call.summary, options);
3458
3460
  const output = objectValue5(call.payload?.output) ?? objectValue5(toolState?.output);
3459
3461
  const error = objectValue5(call.payload?.error) ?? objectValue5(toolState?.error);
3460
- const nestedTaskSteps = arrayValue5(metadata?.steps) ?? arrayValue5(output?.steps) ?? arrayValue5(call.payload?.steps) ?? [];
3462
+ const nestedTaskSteps = nestedTaskStepsFromPayload(call.payload, output);
3461
3463
  const directChanges = extractFileChanges({
3462
3464
  id: call.id,
3463
3465
  toolName: call.toolName,
@@ -3477,11 +3479,7 @@ function filesFromCall(call, options) {
3477
3479
  return extractFileChanges({
3478
3480
  id: stringValue6(step.toolUseId) ?? stringValue6(step.id) ?? `${call.id}:step:${index + 1}`,
3479
3481
  toolName: stringValue6(step.toolName) ?? stringValue6(step.tool_name) ?? stringValue6(step.name) ?? null,
3480
- statusKind: firstNonEmptyString(
3481
- stringValue6(step.statusKind),
3482
- stringValue6(step.status),
3483
- stringValue6(step.toolStatus)
3484
- ) ?? call.statusKind ?? null,
3482
+ statusKind: nestedTaskStepStatusKind(step, call.statusKind ?? null),
3485
3483
  occurredAtUnixMs: numberValue2(step.occurredAtUnixMs) ?? numberValue2(step.occurred_at_unix_ms) ?? call.occurredAtUnixMs ?? null,
3486
3484
  payload: objectValue5(step.payload),
3487
3485
  input: objectValue5(step.toolInput) ?? objectValue5(step.tool_input),
@@ -3492,6 +3490,125 @@ function filesFromCall(call, options) {
3492
3490
  });
3493
3491
  return [...directChanges, ...nestedChanges];
3494
3492
  }
3493
+ function patchBatchesFromCalls(calls, options) {
3494
+ return calls.flatMap((call) => patchBatchesFromCall(call, options));
3495
+ }
3496
+ function patchBatchesFromCall(call, options) {
3497
+ const directBatch = isFailedToolStatus(call.statusKind ?? null) ? [] : patchBatchFromPayload({
3498
+ id: call.id,
3499
+ payload: call.payload ?? null,
3500
+ toolInput: null,
3501
+ toolOutput: null,
3502
+ toolError: null,
3503
+ options
3504
+ });
3505
+ const output = objectValue5(call.payload?.output);
3506
+ const nestedTaskSteps = nestedTaskStepsFromPayload(call.payload, output);
3507
+ const nestedBatches = nestedTaskSteps.flatMap((value, index) => {
3508
+ const step = objectValue5(value);
3509
+ if (!step) {
3510
+ return [];
3511
+ }
3512
+ if (isFailedToolStatus(
3513
+ nestedTaskStepStatusKind(step, call.statusKind ?? null)
3514
+ )) {
3515
+ return [];
3516
+ }
3517
+ return patchBatchFromPayload({
3518
+ id: stringValue6(step.toolUseId) ?? stringValue6(step.id) ?? `${call.id}:step:${index + 1}`,
3519
+ payload: objectValue5(step.payload),
3520
+ toolInput: objectValue5(step.toolInput) ?? objectValue5(step.tool_input),
3521
+ toolOutput: objectValue5(step.toolResult) ?? objectValue5(step.tool_result),
3522
+ toolError: objectValue5(step.toolError) ?? objectValue5(step.tool_error),
3523
+ options
3524
+ });
3525
+ });
3526
+ return [...directBatch, ...nestedBatches];
3527
+ }
3528
+ function patchBatchFromPayload(input) {
3529
+ const toolState = objectValue5(input.payload?.tool_state);
3530
+ const metadata = objectValue5(input.payload?.metadata);
3531
+ const payloadInput = input.toolInput ?? objectValue5(input.payload?.input) ?? objectValue5(toolState?.input);
3532
+ const payloadOutput = input.toolOutput ?? objectValue5(input.payload?.output) ?? objectValue5(toolState?.output);
3533
+ const rawInput = objectValue5(payloadInput?.rawInput);
3534
+ const changes = firstFileChangeValue(
3535
+ payloadOutput?.changes,
3536
+ input.payload?.changes,
3537
+ payloadInput?.changes,
3538
+ rawInput?.changes
3539
+ );
3540
+ const patchChanges = patchChangesFromChangeMap(changes);
3541
+ if (patchChanges.length === 0) {
3542
+ return [];
3543
+ }
3544
+ return [
3545
+ {
3546
+ cwd: firstNonEmptyString(
3547
+ stringValue6(input.payload?.cwd),
3548
+ stringValue6(payloadInput?.cwd),
3549
+ stringValue6(rawInput?.cwd),
3550
+ stringValue6(payloadOutput?.cwd),
3551
+ stringValue6(metadata?.cwd),
3552
+ input.options.defaultCwd ?? null
3553
+ ) ?? null,
3554
+ toolCallId: input.id,
3555
+ changes: patchChanges
3556
+ }
3557
+ ];
3558
+ }
3559
+ function patchChangesFromChangeMap(changes) {
3560
+ return fileChangeEntriesFromChanges(changes).flatMap((entry) => {
3561
+ const change = entry.change;
3562
+ const normalizedPath = entry.path.trim();
3563
+ if (!normalizedPath) {
3564
+ return [];
3565
+ }
3566
+ const normalizedType = normalizeChangeType(fileChangeTypeValue(change));
3567
+ const unifiedDiff = firstNonEmptyString(
3568
+ stringValue6(change.unified_diff),
3569
+ stringValue6(change.unifiedDiff),
3570
+ stringValue6(change.diff),
3571
+ stringValue6(change.patch)
3572
+ ) ?? null;
3573
+ let oldString = firstPresentString6(
3574
+ literalStringValue(change.old_string),
3575
+ literalStringValue(change.oldString)
3576
+ );
3577
+ const explicitContent = literalStringValue(change.content);
3578
+ let newString = firstPresentString6(
3579
+ literalStringValue(change.new_string),
3580
+ literalStringValue(change.newString),
3581
+ explicitContent
3582
+ );
3583
+ if (normalizedType === "created" && oldString === null && newString !== null) {
3584
+ oldString = "";
3585
+ }
3586
+ if (normalizedType === "deleted" && oldString === null && newString !== null) {
3587
+ oldString = newString;
3588
+ newString = "";
3589
+ }
3590
+ if (normalizedType === "deleted" && newString === null && oldString !== null) {
3591
+ newString = "";
3592
+ }
3593
+ const content = firstPresentString6(
3594
+ normalizedType === "deleted" ? null : explicitContent,
3595
+ normalizedType === "created" ? newString : null
3596
+ );
3597
+ if (!unifiedDiff && oldString === null && newString === null && content === null) {
3598
+ return [];
3599
+ }
3600
+ return [
3601
+ {
3602
+ path: normalizedPath,
3603
+ changeType: normalizedType ?? inferAgentPatchChangeType(unifiedDiff),
3604
+ unifiedDiff,
3605
+ oldString,
3606
+ newString,
3607
+ content
3608
+ }
3609
+ ];
3610
+ });
3611
+ }
3495
3612
  function summaryPathInput(summary, options) {
3496
3613
  const path = normalizedFilePath(summary, options);
3497
3614
  return path ? { path, summaryPathFallback: true } : null;
@@ -3532,21 +3649,21 @@ function extractFileChanges(input) {
3532
3649
  stringValue6(payload?.patch),
3533
3650
  stringValue6(metadata?.patch)
3534
3651
  ),
3535
- firstNonEmptyString(
3536
- stringValue6(input.output?.oldString),
3537
- stringValue6(input.output?.old_string),
3538
- stringValue6(input.input?.oldString),
3539
- stringValue6(input.input?.old_string)
3652
+ firstPresentString6(
3653
+ literalStringValue(input.output?.oldString),
3654
+ literalStringValue(input.output?.old_string),
3655
+ literalStringValue(input.input?.oldString),
3656
+ literalStringValue(input.input?.old_string)
3540
3657
  ),
3541
- firstNonEmptyString(
3542
- stringValue6(input.output?.newString),
3543
- stringValue6(input.output?.new_string),
3544
- stringValue6(input.input?.newString),
3545
- stringValue6(input.input?.new_string)
3658
+ firstPresentString6(
3659
+ literalStringValue(input.output?.newString),
3660
+ literalStringValue(input.output?.new_string),
3661
+ literalStringValue(input.input?.newString),
3662
+ literalStringValue(input.input?.new_string)
3546
3663
  ),
3547
- firstNonEmptyString(
3548
- stringValue6(input.output?.content),
3549
- stringValue6(input.input?.content)
3664
+ firstPresentString6(
3665
+ literalStringValue(input.output?.content),
3666
+ literalStringValue(input.input?.content)
3550
3667
  ),
3551
3668
  input.options
3552
3669
  );
@@ -3605,25 +3722,25 @@ function extractFileChanges(input) {
3605
3722
  stringValue6(metadata?.patch),
3606
3723
  stringValue6(input.output?.content)
3607
3724
  ) ?? null;
3608
- const oldString = firstNonEmptyString(
3609
- stringValue6(input.output?.oldString),
3610
- stringValue6(input.output?.old_string),
3611
- stringValue6(input.input?.oldString),
3612
- stringValue6(input.input?.old_string)
3613
- ) ?? null;
3614
- const newString = firstNonEmptyString(
3615
- stringValue6(input.output?.newString),
3616
- stringValue6(input.output?.new_string),
3617
- stringValue6(input.input?.newString),
3618
- stringValue6(input.input?.new_string)
3619
- ) ?? null;
3620
- const content = firstNonEmptyString(
3621
- stringValue6(input.input?.content),
3622
- stringValue6(rawInput?.content),
3623
- stringValue6(input.input?.new_source),
3624
- stringValue6(input.output?.content),
3625
- stringValue6(input.output?.new_source)
3626
- ) ?? null;
3725
+ const oldString = firstPresentString6(
3726
+ literalStringValue(input.output?.oldString),
3727
+ literalStringValue(input.output?.old_string),
3728
+ literalStringValue(input.input?.oldString),
3729
+ literalStringValue(input.input?.old_string)
3730
+ );
3731
+ const newString = firstPresentString6(
3732
+ literalStringValue(input.output?.newString),
3733
+ literalStringValue(input.output?.new_string),
3734
+ literalStringValue(input.input?.newString),
3735
+ literalStringValue(input.input?.new_string)
3736
+ );
3737
+ const content = firstPresentString6(
3738
+ literalStringValue(input.input?.content),
3739
+ literalStringValue(rawInput?.content),
3740
+ literalStringValue(input.input?.new_source),
3741
+ literalStringValue(input.output?.content),
3742
+ literalStringValue(input.output?.new_source)
3743
+ );
3627
3744
  const explicitChangeType = normalizeChangeType(
3628
3745
  firstNonEmptyString(
3629
3746
  stringValue6(payload?.fileChangeKind),
@@ -3722,16 +3839,16 @@ function collectChangeMapFiles(messageId, toolName, occurredAtUnixMs, changes, o
3722
3839
  stringValue6(change.diff),
3723
3840
  stringValue6(change.patch)
3724
3841
  );
3725
- let oldString = firstNonEmptyString(
3726
- stringValue6(change.old_string),
3727
- stringValue6(change.oldString)
3728
- ) ?? null;
3729
- const explicitContent = stringValue6(change.content);
3730
- let newString = firstNonEmptyString(
3731
- stringValue6(change.new_string),
3732
- stringValue6(change.newString),
3842
+ let oldString = firstPresentString6(
3843
+ literalStringValue(change.old_string),
3844
+ literalStringValue(change.oldString)
3845
+ );
3846
+ const explicitContent = literalStringValue(change.content);
3847
+ let newString = firstPresentString6(
3848
+ literalStringValue(change.new_string),
3849
+ literalStringValue(change.newString),
3733
3850
  explicitContent
3734
- ) ?? null;
3851
+ );
3735
3852
  if (normalizedType === "created" && oldString === null && newString !== null) {
3736
3853
  oldString = "";
3737
3854
  }
@@ -3742,10 +3859,10 @@ function collectChangeMapFiles(messageId, toolName, occurredAtUnixMs, changes, o
3742
3859
  if (normalizedType === "deleted" && newString === null && oldString !== null) {
3743
3860
  newString = "";
3744
3861
  }
3745
- const content = firstNonEmptyString(
3862
+ const content = firstPresentString6(
3746
3863
  normalizedType === "deleted" ? null : explicitContent,
3747
3864
  normalizedType === "created" ? newString : null
3748
- ) ?? null;
3865
+ );
3749
3866
  if (!unifiedDiff && oldString === null && newString === null && content === null) {
3750
3867
  return [];
3751
3868
  }
@@ -3797,20 +3914,20 @@ function collectContentDiffFiles(messageId, toolName, occurredAtUnixMs, contentI
3797
3914
  stringValue6(relatedChange?.unified_diff),
3798
3915
  stringValue6(relatedChange?.unifiedDiff)
3799
3916
  );
3800
- let oldString = firstNonEmptyString(
3801
- stringValue6(item.oldText),
3802
- stringValue6(item.oldString),
3803
- stringValue6(relatedChange?.old_string),
3804
- stringValue6(relatedChange?.oldString)
3805
- ) ?? null;
3806
- const relatedContent = stringValue6(relatedChange?.content);
3807
- let newString = firstNonEmptyString(
3808
- stringValue6(item.newText),
3809
- stringValue6(item.newString),
3810
- stringValue6(relatedChange?.new_string),
3811
- stringValue6(relatedChange?.newString),
3917
+ let oldString = firstPresentString6(
3918
+ literalStringValue(item.oldText),
3919
+ literalStringValue(item.oldString),
3920
+ literalStringValue(relatedChange?.old_string),
3921
+ literalStringValue(relatedChange?.oldString)
3922
+ );
3923
+ const relatedContent = literalStringValue(relatedChange?.content);
3924
+ let newString = firstPresentString6(
3925
+ literalStringValue(item.newText),
3926
+ literalStringValue(item.newString),
3927
+ literalStringValue(relatedChange?.new_string),
3928
+ literalStringValue(relatedChange?.newString),
3812
3929
  relatedContent
3813
- ) ?? null;
3930
+ );
3814
3931
  if (normalizedType === "created" && oldString === null && newString !== null) {
3815
3932
  oldString = "";
3816
3933
  }
@@ -3821,7 +3938,10 @@ function collectContentDiffFiles(messageId, toolName, occurredAtUnixMs, contentI
3821
3938
  if (normalizedType === "deleted" && newString === null && oldString !== null) {
3822
3939
  newString = "";
3823
3940
  }
3824
- const explicitContent = firstNonEmptyString(stringValue6(item.content), relatedContent) ?? null;
3941
+ const explicitContent = firstPresentString6(
3942
+ literalStringValue(item.content),
3943
+ relatedContent
3944
+ );
3825
3945
  const inferredType = normalizedType ?? inferChangeTypeFromToolContent(
3826
3946
  toolName,
3827
3947
  normalizedType === "deleted" ? null : explicitContent,
@@ -3831,10 +3951,10 @@ function collectContentDiffFiles(messageId, toolName, occurredAtUnixMs, contentI
3831
3951
  if (inferredType === "created" && oldString === null && newString !== null) {
3832
3952
  oldString = "";
3833
3953
  }
3834
- const content = firstNonEmptyString(
3954
+ const content = firstPresentString6(
3835
3955
  inferredType === "deleted" ? null : explicitContent,
3836
3956
  inferredType === "created" ? newString : null
3837
- ) ?? null;
3957
+ );
3838
3958
  if (!unifiedDiff && oldString === null && newString === null && content === null) {
3839
3959
  return [];
3840
3960
  }
@@ -4037,7 +4157,7 @@ function normalizeChangeType(value) {
4037
4157
  function inferChangeTypeFromToolContent(toolName, content, oldString, newString) {
4038
4158
  const normalizedToolName = normalizeToolName4(toolName);
4039
4159
  if (normalizedToolName === "write" || normalizedToolName === "writefile") {
4040
- return content || newString ? "created" : null;
4160
+ return content !== null || newString !== null ? "created" : null;
4041
4161
  }
4042
4162
  if (oldString !== null || newString !== null) {
4043
4163
  return "modified";
@@ -4072,6 +4192,17 @@ function firstFileChangeValue(...values) {
4072
4192
  function arrayValue5(value) {
4073
4193
  return Array.isArray(value) ? value : null;
4074
4194
  }
4195
+ function nestedTaskStepsFromPayload(payload, output) {
4196
+ const metadata = objectValue5(payload?.metadata);
4197
+ return arrayValue5(metadata?.steps) ?? arrayValue5(output?.steps) ?? arrayValue5(payload?.steps) ?? [];
4198
+ }
4199
+ function nestedTaskStepStatusKind(step, fallback) {
4200
+ return firstNonEmptyString(
4201
+ stringValue6(step.statusKind),
4202
+ stringValue6(step.status),
4203
+ stringValue6(step.toolStatus)
4204
+ ) ?? fallback ?? null;
4205
+ }
4075
4206
  function firstLocationPath(value) {
4076
4207
  if (!value) {
4077
4208
  return null;
@@ -4100,6 +4231,9 @@ function firstPathValue(value) {
4100
4231
  function stringValue6(value) {
4101
4232
  return typeof value === "string" && value.trim() ? value.trim() : null;
4102
4233
  }
4234
+ function literalStringValue(value) {
4235
+ return typeof value === "string" ? value : null;
4236
+ }
4103
4237
  function numberValue2(value) {
4104
4238
  return typeof value === "number" && Number.isFinite(value) ? value : null;
4105
4239
  }
@@ -4111,6 +4245,14 @@ function firstNonEmptyString(...values) {
4111
4245
  }
4112
4246
  return null;
4113
4247
  }
4248
+ function firstPresentString6(...values) {
4249
+ for (const value of values) {
4250
+ if (typeof value === "string") {
4251
+ return value;
4252
+ }
4253
+ }
4254
+ return null;
4255
+ }
4114
4256
 
4115
4257
  // shared/agentConversation/projection/agentConversationProjection.ts
4116
4258
  var RENDER_IRRELEVANT_TRANSCRIPT_ROW_FIELDS = /* @__PURE__ */ new Set(["occurredAtUnixMs"]);
@@ -4887,6 +5029,186 @@ var Button = React.forwardRef(
4887
5029
  );
4888
5030
  Button.displayName = "Button";
4889
5031
 
5032
+ // shared/errors/appError.ts
5033
+ function createMessageMap() {
5034
+ return {
5035
+ "common.invalid_input": "The request was invalid.",
5036
+ "common.approved_path_required": "The selected path is outside approved workspaces.",
5037
+ "common.unavailable": "This feature is unavailable.",
5038
+ "common.unexpected": "Something went wrong. Please try again.",
5039
+ "session.not_found": "Session not found.",
5040
+ "control_surface.unauthorized": "Unauthorized request.",
5041
+ "workspace.select_directory_failed": "Unable to open the directory picker.",
5042
+ "workspace.select_files_failed": "Unable to open the file picker.",
5043
+ "workspace.ensure_directory_failed": "Unable to create the directory.",
5044
+ "workspace.import_files_failed": "Unable to import the selected files.",
5045
+ "workspace.read_file_failed": "Unable to load the selected file.",
5046
+ "workspace.write_file_failed": "Unable to save the selected file.",
5047
+ "workspace.export_file_failed": "Unable to export the selected file.",
5048
+ "workspace.copy_path_failed": "Unable to copy the path.",
5049
+ "workspace.host_unsupported": "This Mac cannot run the workspace environment required by tsh. Please use macOS 13 or later on Apple Silicon (or an Intel Mac that supports Apple Virtualization), and ensure virtualization is not blocked by MDM.",
5050
+ "workspace.runtime_artifact_unavailable": "Unable to prepare the workspace environment required by tsh. Check your network connection and try again.",
5051
+ "runtime.guest_agent_lane_unavailable": "The workspace environment connection is unavailable. Refresh and try again.",
5052
+ "workspace.room_full": "Room is full.",
5053
+ "workspace.room_delete_forbidden": "Only the space owner can delete this space.",
5054
+ "workspace.room_delete_not_found": "This space no longer exists.",
5055
+ "filesystem.create_directory_failed": "Unable to create the directory.",
5056
+ "filesystem.read_file_bytes_failed": "Unable to read the file.",
5057
+ "filesystem.read_file_text_failed": "Unable to read the file.",
5058
+ "filesystem.write_file_text_failed": "Unable to save the file.",
5059
+ "filesystem.copy_entry_failed": "Unable to copy the file or folder.",
5060
+ "filesystem.move_entry_failed": "Unable to move the file or folder.",
5061
+ "filesystem.rename_entry_failed": "Unable to rename the file or folder.",
5062
+ "filesystem.delete_entry_failed": "Unable to delete the file or folder.",
5063
+ "filesystem.read_directory_failed": "Unable to load the directory.",
5064
+ "filesystem.stat_failed": "Unable to read file details.",
5065
+ "terminal.spawn_failed": "Unable to start the terminal.",
5066
+ "terminal.write_failed": "Unable to write to the terminal.",
5067
+ "terminal.resize_failed": "Unable to resize the terminal.",
5068
+ "terminal.close_failed": "Unable to close the terminal.",
5069
+ "terminal.attach_failed": "Unable to attach the terminal session.",
5070
+ "terminal.detach_failed": "Unable to detach the terminal session.",
5071
+ "terminal.snapshot_failed": "Unable to read terminal output.",
5072
+ "agent.list_models_failed": "Unable to load models for this provider.",
5073
+ "agent.launch_failed": "Unable to start the agent.",
5074
+ "agent.read_last_message_failed": "Unable to read the last agent message.",
5075
+ "agent.provider_session_not_found": "The previous agent session can no longer be restored.",
5076
+ "agent.resume_session_not_local": "The previous agent session is not available on this machine.",
5077
+ "agent.resume_session_resolve_failed": "Unable to resolve the previous agent session.",
5078
+ "agent.settings_require_new_session": "This model can only be used in a new session to preserve context.",
5079
+ "task.suggest_title_failed": "Unable to generate task details.",
5080
+ "persistence.unavailable": "Storage is unavailable; changes will not be saved.",
5081
+ "persistence.quota_exceeded": "Storage quota was exceeded.",
5082
+ "persistence.payload_too_large": "Workspace state is too large to save.",
5083
+ "persistence.io_failed": "Unable to save data to storage.",
5084
+ "persistence.invalid_state": "The workspace state could not be saved.",
5085
+ "persistence.invalid_node_id": "The terminal history could not be saved.",
5086
+ "update.get_state_failed": "Unable to read the update status.",
5087
+ "update.configure_failed": "Unable to apply update settings.",
5088
+ "update.check_failed": "Unable to check for updates.",
5089
+ "update.download_failed": "Unable to download the update.",
5090
+ "update.install_failed": "Unable to install the update.",
5091
+ PACKAGE_DOWNLOAD_INTERRUPTED: "The agent package download was interrupted. Check your network connection and retry.",
5092
+ PACKAGE_DOWNLOAD_HTTP_STATUS: "The agent package server rejected the download. Retry later or contact support.",
5093
+ PACKAGE_DOWNLOAD_INVALID: "The downloaded agent package failed integrity checks. Retry the download.",
5094
+ PACKAGE_DOWNLOAD_DISK_ERROR: "Unable to write the agent package cache. Check disk permissions and free space."
5095
+ };
5096
+ }
5097
+ var APP_ERROR_MESSAGES = createMessageMap();
5098
+ function createAppErrorDescriptor(code, options = {}) {
5099
+ return {
5100
+ code,
5101
+ ...options.params ? { params: options.params } : {},
5102
+ ...options.debugMessage ? { debugMessage: options.debugMessage } : {}
5103
+ };
5104
+ }
5105
+ function isAppErrorDescriptor(value) {
5106
+ if (!value || typeof value !== "object") {
5107
+ return false;
5108
+ }
5109
+ const record = value;
5110
+ return typeof record.code === "string" && record.code in APP_ERROR_MESSAGES;
5111
+ }
5112
+ function getAppErrorCode(error) {
5113
+ if (error instanceof TshAppError) {
5114
+ return error.code;
5115
+ }
5116
+ if (isAppErrorDescriptor(error)) {
5117
+ return error.code;
5118
+ }
5119
+ if (error && typeof error === "object") {
5120
+ const code = error.code;
5121
+ if (typeof code === "string" && code in APP_ERROR_MESSAGES) {
5122
+ return code;
5123
+ }
5124
+ }
5125
+ return null;
5126
+ }
5127
+ var TshAppError = class extends Error {
5128
+ code;
5129
+ params;
5130
+ debugMessage;
5131
+ constructor(descriptor) {
5132
+ super(formatAppErrorMessage(descriptor));
5133
+ this.name = "TshAppError";
5134
+ this.code = descriptor.code;
5135
+ this.params = descriptor.params;
5136
+ this.debugMessage = descriptor.debugMessage;
5137
+ }
5138
+ toDescriptor() {
5139
+ return createAppErrorDescriptor(this.code, {
5140
+ params: this.params,
5141
+ debugMessage: this.debugMessage
5142
+ });
5143
+ }
5144
+ };
5145
+ function formatAppErrorMessage(error) {
5146
+ const descriptor = error instanceof TshAppError ? error.toDescriptor() : error;
5147
+ return APP_ERROR_MESSAGES[descriptor.code] ?? APP_ERROR_MESSAGES["common.unexpected"];
5148
+ }
5149
+
5150
+ // app/renderer/shell/utils/format.ts
5151
+ var shortDateTimeFormatterByLanguage = /* @__PURE__ */ new Map();
5152
+ var weekdayTimeFormatterByLanguage = /* @__PURE__ */ new Map();
5153
+ function getShortDateTimeFormatter(language) {
5154
+ const cached = shortDateTimeFormatterByLanguage.get(language);
5155
+ if (cached) {
5156
+ return cached;
5157
+ }
5158
+ const formatter = new Intl.DateTimeFormat(language, {
5159
+ month: language === "en" ? "short" : "numeric",
5160
+ day: "numeric",
5161
+ hour: "2-digit",
5162
+ minute: "2-digit",
5163
+ hourCycle: "h23"
5164
+ });
5165
+ shortDateTimeFormatterByLanguage.set(language, formatter);
5166
+ return formatter;
5167
+ }
5168
+ function getWeekdayTimeFormatter(language) {
5169
+ const cached = weekdayTimeFormatterByLanguage.get(language);
5170
+ if (cached) {
5171
+ return cached;
5172
+ }
5173
+ const formatter = new Intl.DateTimeFormat(language, {
5174
+ weekday: "long",
5175
+ hour: "2-digit",
5176
+ minute: "2-digit",
5177
+ hourCycle: "h23"
5178
+ });
5179
+ weekdayTimeFormatterByLanguage.set(language, formatter);
5180
+ return formatter;
5181
+ }
5182
+ function toLocalWeekdayTime(value, language = getActiveUiLanguage()) {
5183
+ const formatter = getWeekdayTimeFormatter(language);
5184
+ const parts = formatter.formatToParts(value);
5185
+ const weekday = parts.find((part) => part.type === "weekday")?.value;
5186
+ const hour = parts.find((part) => part.type === "hour")?.value;
5187
+ const minute = parts.find((part) => part.type === "minute")?.value;
5188
+ if (weekday && hour && minute) {
5189
+ return `${weekday} ${hour}:${minute}`;
5190
+ }
5191
+ return formatter.format(value);
5192
+ }
5193
+ function toLocalShortDateTime(value, language = getActiveUiLanguage()) {
5194
+ return getShortDateTimeFormatter(language).format(value);
5195
+ }
5196
+ function formatAgentMessageTimestamp(unix, language = getActiveUiLanguage(), now = /* @__PURE__ */ new Date()) {
5197
+ if (unix == null || !Number.isFinite(unix)) {
5198
+ return null;
5199
+ }
5200
+ const value = unix > 1e12 ? unix : unix * 1e3;
5201
+ const messageDate = new Date(value);
5202
+ const startOfWeek = new Date(now);
5203
+ const daysSinceMonday = (startOfWeek.getDay() + 6) % 7;
5204
+ startOfWeek.setHours(0, 0, 0, 0);
5205
+ startOfWeek.setDate(startOfWeek.getDate() - daysSinceMonday);
5206
+ if (messageDate.getTime() >= startOfWeek.getTime()) {
5207
+ return toLocalWeekdayTime(value, language);
5208
+ }
5209
+ return toLocalShortDateTime(value, language);
5210
+ }
5211
+
4890
5212
  // shared/agentConversation/components/AgentPlanCard.tsx
4891
5213
  import { useCallback, useRef, useState } from "react";
4892
5214
  import { Check, ChevronDown, ChevronUp, Copy, ListChecks } from "lucide-react";
@@ -6808,6 +7130,7 @@ function AgentMessageBlock({
6808
7130
  AgentCopyableMessageGroup,
6809
7131
  {
6810
7132
  copyText: message.copyText ?? null,
7133
+ occurredAtUnixMs: message.occurredAtUnixMs,
6811
7134
  speaker: row.speaker,
6812
7135
  onCopyMessageText: handleCopyMessageText,
6813
7136
  children: [
@@ -6824,6 +7147,7 @@ function AgentMessageBlock({
6824
7147
  AgentCopyableMessageGroup,
6825
7148
  {
6826
7149
  copyText,
7150
+ occurredAtUnixMs: message.occurredAtUnixMs,
6827
7151
  speaker: row.speaker,
6828
7152
  onCopyMessageText: handleCopyMessageText,
6829
7153
  children: content
@@ -6840,19 +7164,24 @@ function AgentMessageBlock({
6840
7164
  function AgentCopyableMessageGroup({
6841
7165
  children,
6842
7166
  copyText,
7167
+ occurredAtUnixMs,
6843
7168
  onCopyMessageText,
6844
7169
  speaker
6845
7170
  }) {
6846
7171
  "use memo";
7172
+ const timestamp = formatAgentMessageTimestamp(occurredAtUnixMs);
6847
7173
  return /* @__PURE__ */ jsxs7("div", { className: AgentGUIConversation_styles_default.messageGroup, "data-agent-message-speaker": speaker, children: [
6848
7174
  children,
6849
- copyText ? /* @__PURE__ */ jsx12(
6850
- AgentMessageCopyButton,
6851
- {
6852
- copyText,
6853
- onCopyMessageText
6854
- }
6855
- ) : null
7175
+ timestamp || copyText ? /* @__PURE__ */ jsxs7("div", { className: AgentGUIConversation_styles_default.messageFooter, children: [
7176
+ timestamp ? /* @__PURE__ */ jsx12("span", { className: AgentGUIConversation_styles_default.messageTimestamp, children: timestamp }) : null,
7177
+ copyText ? /* @__PURE__ */ jsx12(
7178
+ AgentMessageCopyButton,
7179
+ {
7180
+ copyText,
7181
+ onCopyMessageText
7182
+ }
7183
+ ) : null
7184
+ ] }) : null
6856
7185
  ] });
6857
7186
  }
6858
7187
  function AgentMessageCopyButton({
@@ -11922,8 +12251,21 @@ function renderToolCard(call, onLinkClick, previewMode = false, showRawTimelineJ
11922
12251
  }
11923
12252
 
11924
12253
  // shared/agentConversation/components/AgentTurnSummaryRow.tsx
11925
- import { useMemo as useMemo5, useState as useState16 } from "react";
11926
- import { ChevronDown as ChevronDown7, ChevronRight as ChevronRight7 } from "lucide-react";
12254
+ import { useEffect as useEffect10, useMemo as useMemo5, useState as useState16 } from "react";
12255
+ import {
12256
+ ChevronDown as ChevronDown7,
12257
+ ChevronRight as ChevronRight7,
12258
+ LoaderCircle as LoaderCircle2,
12259
+ Redo2,
12260
+ Undo2
12261
+ } from "lucide-react";
12262
+ import {
12263
+ Tooltip,
12264
+ TooltipContent,
12265
+ TooltipProvider,
12266
+ TooltipTrigger,
12267
+ toast
12268
+ } from "@tutti-os/ui-system";
11927
12269
 
11928
12270
  // app/renderer/components/icons/DirectLinedIcon.tsx
11929
12271
  import { jsx as jsx53 } from "react/jsx-runtime";
@@ -11950,6 +12292,195 @@ function DirectLinedIcon(props) {
11950
12292
  );
11951
12293
  }
11952
12294
 
12295
+ // shared/agentConversation/rules/agentTurnSummaryPatchDiff.ts
12296
+ function buildAgentTurnSummaryPatchDiff(batch) {
12297
+ return batch.changes.map((change) => patchChangeToUnifiedDiff(change, batch.cwd)).filter((diff) => diff.trim().length > 0).join("\n");
12298
+ }
12299
+ function patchChangeToUnifiedDiff(change, cwd) {
12300
+ const path = patchPathRelativeToCwd(change.path, cwd);
12301
+ const rawDiff = normalizeAgentPatchText(change.unifiedDiff ?? "").trim();
12302
+ if (rawDiff && looksLikeUnifiedDiff(rawDiff)) {
12303
+ return ensureTrailingNewline(
12304
+ wrapUnifiedDiff(path, change.changeType, rawDiff)
12305
+ );
12306
+ }
12307
+ if (change.changeType === "created") {
12308
+ return fileContentPatch(
12309
+ path,
12310
+ "created",
12311
+ change.content ?? change.newString ?? rawDiff
12312
+ );
12313
+ }
12314
+ if (change.changeType === "deleted") {
12315
+ return fileContentPatch(
12316
+ path,
12317
+ "deleted",
12318
+ change.oldString ?? change.content ?? rawDiff
12319
+ );
12320
+ }
12321
+ if (change.oldString != null || change.newString != null) {
12322
+ return modifiedFilePatch(
12323
+ path,
12324
+ change.oldString ?? "",
12325
+ change.newString ?? ""
12326
+ );
12327
+ }
12328
+ return "";
12329
+ }
12330
+ function wrapUnifiedDiff(path, changeType, diff) {
12331
+ if (diff.startsWith("diff --git ")) {
12332
+ return diff;
12333
+ }
12334
+ const headers = [`diff --git a/${path} b/${path}`];
12335
+ if (changeType === "created") {
12336
+ headers.push("new file mode 100644", "--- /dev/null", `+++ b/${path}`);
12337
+ } else if (changeType === "deleted") {
12338
+ headers.push("deleted file mode 100644", `--- a/${path}`, "+++ /dev/null");
12339
+ } else if (!diff.startsWith("--- ") && !diff.includes("\n--- ")) {
12340
+ headers.push(`--- a/${path}`, `+++ b/${path}`);
12341
+ }
12342
+ return [...headers, diff].join("\n");
12343
+ }
12344
+ function fileContentPatch(path, changeType, content) {
12345
+ const lines = splitPatchContentLines(content);
12346
+ const count = Math.max(lines.length, 1);
12347
+ const prefix = changeType === "created" ? "+" : "-";
12348
+ const body = lines.length > 0 ? lines.map((line) => `${prefix}${line}`) : [`${prefix}`];
12349
+ const header = changeType === "created" ? [
12350
+ `diff --git a/${path} b/${path}`,
12351
+ "new file mode 100644",
12352
+ "--- /dev/null",
12353
+ `+++ b/${path}`,
12354
+ `@@ -0,0 +1,${count} @@`
12355
+ ] : [
12356
+ `diff --git a/${path} b/${path}`,
12357
+ "deleted file mode 100644",
12358
+ `--- a/${path}`,
12359
+ "+++ /dev/null",
12360
+ `@@ -1,${count} +0,0 @@`
12361
+ ];
12362
+ return ensureTrailingNewline([...header, ...body].join("\n"));
12363
+ }
12364
+ function modifiedFilePatch(path, oldContent, newContent) {
12365
+ const oldLines = splitPatchContentLines(oldContent);
12366
+ const newLines = splitPatchContentLines(newContent);
12367
+ const oldCount = Math.max(oldLines.length, 1);
12368
+ const newCount = Math.max(newLines.length, 1);
12369
+ return ensureTrailingNewline(
12370
+ [
12371
+ `diff --git a/${path} b/${path}`,
12372
+ `--- a/${path}`,
12373
+ `+++ b/${path}`,
12374
+ `@@ -1,${oldCount} +1,${newCount} @@`,
12375
+ ...oldLines.map((line) => `-${line}`),
12376
+ ...newLines.map((line) => `+${line}`)
12377
+ ].join("\n")
12378
+ );
12379
+ }
12380
+ function patchPathRelativeToCwd(path, cwd) {
12381
+ const normalizedPath = normalizePathForPatch(path);
12382
+ const normalizedCwd = normalizePathForPatch(cwd ?? "");
12383
+ if (normalizedPath.startsWith("/") && normalizedCwd && normalizedPath.startsWith(`${normalizedCwd}/`)) {
12384
+ return normalizedPath.slice(normalizedCwd.length + 1);
12385
+ }
12386
+ return normalizedPath.replace(/^\/+/, "");
12387
+ }
12388
+ function normalizePathForPatch(path) {
12389
+ return path.trim().replaceAll("\\", "/").replace(/\/+$/, "");
12390
+ }
12391
+ function looksLikeUnifiedDiff(value) {
12392
+ return value.startsWith("diff --git ") || value.startsWith("@@ ") || value.startsWith("--- ") || value.includes("\n@@ ");
12393
+ }
12394
+ function splitPatchContentLines(content) {
12395
+ if (!content) {
12396
+ return [];
12397
+ }
12398
+ return content.replace(/\r\n/g, "\n").replace(/\n$/, "").split("\n");
12399
+ }
12400
+ function ensureTrailingNewline(value) {
12401
+ return value.endsWith("\n") ? value : `${value}
12402
+ `;
12403
+ }
12404
+
12405
+ // shared/agentConversation/rules/agentTurnSummaryPatchRuntime.ts
12406
+ function fileCanBuildPatch(file) {
12407
+ return file.unifiedDiff != null || file.content != null || file.oldString != null || file.newString != null;
12408
+ }
12409
+ function patchBatchDirectoryCwd(cwd, changes) {
12410
+ const normalizedCwd = normalizePatchHostPath(cwd ?? "");
12411
+ if (!normalizedCwd) {
12412
+ return null;
12413
+ }
12414
+ const cwdIsChangedFilePath = changes.some(
12415
+ (change) => normalizePatchHostPath(change.path) === normalizedCwd
12416
+ );
12417
+ return cwdIsChangedFilePath ? dirnameForPatchHostPath(normalizedCwd) : cwd;
12418
+ }
12419
+ function resolvePatchExecutionCwd(cwd, workspaceRoot) {
12420
+ const normalizedCwd = normalizePatchHostPath(cwd ?? "");
12421
+ const normalizedRoot = normalizePatchHostPath(workspaceRoot ?? "");
12422
+ if (!normalizedCwd) {
12423
+ return normalizedRoot || null;
12424
+ }
12425
+ if (!normalizedRoot) {
12426
+ return normalizedCwd;
12427
+ }
12428
+ if (isSyntheticWorkspacePath(normalizedRoot)) {
12429
+ return normalizedCwd;
12430
+ }
12431
+ if (normalizedCwd === "/workspace") {
12432
+ return normalizedRoot;
12433
+ }
12434
+ if (normalizedCwd.startsWith("/workspace/")) {
12435
+ return `${normalizedRoot}/${normalizedCwd.slice("/workspace/".length)}`;
12436
+ }
12437
+ return normalizedCwd;
12438
+ }
12439
+ function resolvePatchDiffCwd({
12440
+ sourceCwd,
12441
+ executionCwd,
12442
+ changes
12443
+ }) {
12444
+ const normalizedSourceCwd = normalizePatchHostPath(sourceCwd ?? "");
12445
+ const normalizedExecutionCwd = normalizePatchHostPath(executionCwd ?? "");
12446
+ if (!isSyntheticWorkspacePath(normalizedSourceCwd) || !normalizedExecutionCwd) {
12447
+ return sourceCwd;
12448
+ }
12449
+ const hasSyntheticChangePath = changes.some(
12450
+ (change) => isInsideOrEqualPatchPath(
12451
+ normalizePatchHostPath(change.path),
12452
+ normalizedSourceCwd
12453
+ )
12454
+ );
12455
+ const hasHostChangePath = changes.some(
12456
+ (change) => isInsideOrEqualPatchPath(
12457
+ normalizePatchHostPath(change.path),
12458
+ normalizedExecutionCwd
12459
+ )
12460
+ );
12461
+ return hasHostChangePath && !hasSyntheticChangePath ? normalizedExecutionCwd : sourceCwd;
12462
+ }
12463
+ function normalizePatchHostPath(path) {
12464
+ return path.trim().replaceAll("\\", "/").replace(/\/+$/, "");
12465
+ }
12466
+ function dirnameForPatchHostPath(path) {
12467
+ const normalized = normalizePatchHostPath(path);
12468
+ const index = normalized.lastIndexOf("/");
12469
+ if (index < 0) {
12470
+ return "";
12471
+ }
12472
+ if (index === 0) {
12473
+ return "/";
12474
+ }
12475
+ return normalized.slice(0, index);
12476
+ }
12477
+ function isSyntheticWorkspacePath(path) {
12478
+ return path === "/workspace" || path.startsWith("/workspace/");
12479
+ }
12480
+ function isInsideOrEqualPatchPath(path, root) {
12481
+ return path === root || path.startsWith(`${root}/`);
12482
+ }
12483
+
11953
12484
  // shared/agentConversation/components/AgentTurnSummaryRow.tsx
11954
12485
  import { jsx as jsx54, jsxs as jsxs41 } from "react/jsx-runtime";
11955
12486
  function AgentTurnSummaryRow({
@@ -11962,29 +12493,201 @@ function AgentTurnSummaryRow({
11962
12493
  {}
11963
12494
  );
11964
12495
  const [showAllFiles, setShowAllFiles] = useState16(false);
12496
+ const [patchAction, setPatchAction] = useState16("undo");
12497
+ const [patchPending, setPatchPending] = useState16(false);
12498
+ const [patchSupportState, setPatchSupportState] = useState16(null);
12499
+ const agentHostApi = useOptionalAgentHostApi();
11965
12500
  const aggregateStats = useMemo5(
11966
12501
  () => summarizeRowDiff(row.files),
11967
12502
  [row.files]
11968
12503
  );
12504
+ const patchBatches = useMemo5(() => {
12505
+ const fileFallbackBatches = row.files.flatMap(
12506
+ (file) => fileCanBuildPatch(file) ? [
12507
+ {
12508
+ changes: [
12509
+ {
12510
+ path: file.path,
12511
+ changeType: file.changeType,
12512
+ unifiedDiff: file.unifiedDiff,
12513
+ oldString: file.oldString ?? null,
12514
+ newString: file.newString ?? null,
12515
+ content: file.content ?? null
12516
+ }
12517
+ ],
12518
+ cwd: fallbackPatchFileCwd(file.path, workspaceRoot),
12519
+ toolCallId: `${row.id}:${file.messageId}:unified-diff`
12520
+ }
12521
+ ] : []
12522
+ );
12523
+ const sourceBatches = row.patchBatches && row.patchBatches.length > 0 ? row.patchBatches : fileFallbackBatches;
12524
+ const batches = buildExecutablePatchBatches(sourceBatches, workspaceRoot);
12525
+ if (batches.length > 0 || sourceBatches === fileFallbackBatches) {
12526
+ return batches;
12527
+ }
12528
+ return buildExecutablePatchBatches(fileFallbackBatches, workspaceRoot);
12529
+ }, [row.files, row.id, row.patchBatches, workspaceRoot]);
12530
+ const canRenderPatchButton = Boolean(
12531
+ agentHostApi?.workspace.applyGitPatch && row.files.length > 0
12532
+ );
12533
+ const canApplyPatch = Boolean(
12534
+ agentHostApi?.workspace.applyGitPatch && patchBatches.length > 0
12535
+ );
12536
+ const patchSupportCwds = useMemo5(
12537
+ () => Array.from(new Set(patchBatches.map((batch) => batch.cwd))).sort(),
12538
+ [patchBatches]
12539
+ );
12540
+ const patchSupportKey = patchSupportCwds.join("\n");
12541
+ const resolveGitPatchSupport = agentHostApi?.workspace.resolveGitPatchSupport;
12542
+ useEffect10(() => {
12543
+ if (!resolveGitPatchSupport || patchSupportCwds.length === 0) {
12544
+ setPatchSupportState(null);
12545
+ return;
12546
+ }
12547
+ let disposed = false;
12548
+ setPatchSupportState({ key: patchSupportKey, status: "checking" });
12549
+ void (async () => {
12550
+ try {
12551
+ for (const cwd of patchSupportCwds) {
12552
+ const result = await resolveGitPatchSupport({ cwd });
12553
+ if (!result.supported) {
12554
+ if (!disposed) {
12555
+ setPatchSupportState({
12556
+ key: patchSupportKey,
12557
+ status: "unsupported"
12558
+ });
12559
+ }
12560
+ return;
12561
+ }
12562
+ }
12563
+ if (!disposed) {
12564
+ setPatchSupportState({ key: patchSupportKey, status: "supported" });
12565
+ }
12566
+ } catch {
12567
+ if (!disposed) {
12568
+ setPatchSupportState({ key: patchSupportKey, status: "supported" });
12569
+ }
12570
+ }
12571
+ })();
12572
+ return () => {
12573
+ disposed = true;
12574
+ };
12575
+ }, [patchSupportCwds, patchSupportKey, resolveGitPatchSupport]);
11969
12576
  const visibleFiles = row.files.slice(0, 3);
11970
12577
  const hiddenFiles = row.files.slice(3);
11971
12578
  const hiddenFileCount = hiddenFiles.length;
12579
+ const patchActionLabel = patchAction === "undo" ? translate("agentHost.agentGui.turnSummaryUndo") : translate("agentHost.agentGui.turnSummaryReapply");
12580
+ const isPatchSupportChecking = Boolean(
12581
+ resolveGitPatchSupport && patchSupportCwds.length > 0 && (patchSupportState?.key !== patchSupportKey || patchSupportState.status === "checking")
12582
+ );
12583
+ const isPatchUnsupported = Boolean(
12584
+ patchSupportState?.key === patchSupportKey && patchSupportState.status === "unsupported"
12585
+ );
12586
+ const patchDisabledReason = isPatchUnsupported ? translate("agentHost.agentGui.turnSummaryGitRequired") : isPatchSupportChecking ? translate("agentHost.agentGui.turnSummaryCheckingGit") : !canApplyPatch ? translate("agentHost.agentGui.turnSummaryPatchUnavailable") : null;
12587
+ const isPatchActionDisabled = Boolean(
12588
+ patchPending || isPatchSupportChecking || isPatchUnsupported || !canApplyPatch
12589
+ );
12590
+ const handlePatchAction = async (event) => {
12591
+ event.stopPropagation();
12592
+ if (!agentHostApi?.workspace.applyGitPatch || isPatchActionDisabled) {
12593
+ return;
12594
+ }
12595
+ const orderedBatches = patchAction === "undo" ? [...patchBatches].reverse() : patchBatches;
12596
+ if (orderedBatches.length === 0) {
12597
+ return;
12598
+ }
12599
+ setPatchPending(true);
12600
+ let changed = false;
12601
+ const failureMessage = patchAction === "undo" ? translate("agentHost.agentGui.turnSummaryUndoFailed") : translate("agentHost.agentGui.turnSummaryReapplyFailed");
12602
+ try {
12603
+ for (const batch of orderedBatches) {
12604
+ const result = await agentHostApi.workspace.applyGitPatch({
12605
+ cwd: batch.cwd,
12606
+ diff: batch.diff,
12607
+ revert: patchAction === "undo"
12608
+ });
12609
+ if (result.status === "success" || result.appliedPaths.length > 0 || result.conflictedPaths.length > 0) {
12610
+ changed = true;
12611
+ }
12612
+ if (result.status !== "success") {
12613
+ showPatchFailureToast(agentHostApi, failureMessage);
12614
+ return;
12615
+ }
12616
+ }
12617
+ setPatchAction((current) => current === "undo" ? "reapply" : "undo");
12618
+ } catch {
12619
+ showPatchFailureToast(agentHostApi, failureMessage);
12620
+ } finally {
12621
+ setPatchPending(false);
12622
+ if (changed) {
12623
+ window.dispatchEvent(new CustomEvent("tutti-agent-git-patch-applied"));
12624
+ }
12625
+ }
12626
+ };
11972
12627
  return /* @__PURE__ */ jsx54("section", { className: "workspace-agents-status-panel__detail-turn-summary", children: /* @__PURE__ */ jsxs41("div", { className: "agent-turn-summary-card w-full overflow-hidden rounded-[8px] text-[var(--text-primary)]", children: [
11973
- /* @__PURE__ */ jsx54("div", { className: "flex items-start gap-3 px-4 py-3", children: /* @__PURE__ */ jsx54("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxs41("div", { className: "flex min-w-0 items-center gap-3", children: [
11974
- /* @__PURE__ */ jsx54("div", { className: "min-w-0 text-[15px] font-semibold leading-5 tracking-[0] text-[var(--text-primary)]", children: translate("agentHost.agentGui.turnSummaryFilesChanged", {
11975
- count: row.fileCount
11976
- }) }),
11977
- /* @__PURE__ */ jsxs41("div", { className: "inline-flex shrink-0 items-center gap-2.5 text-[11px] font-semibold", children: [
11978
- aggregateStats.added > 0 ? /* @__PURE__ */ jsxs41("span", { className: "workspace-agents-status-panel__detail-tool-diff-added", children: [
11979
- "+",
11980
- aggregateStats.added
11981
- ] }) : null,
11982
- aggregateStats.removed > 0 ? /* @__PURE__ */ jsxs41("span", { className: "workspace-agents-status-panel__detail-tool-diff-removed", children: [
11983
- "-",
11984
- aggregateStats.removed
11985
- ] }) : null
11986
- ] })
11987
- ] }) }) }),
12628
+ /* @__PURE__ */ jsxs41("div", { className: "flex items-start gap-3 px-4 py-3", children: [
12629
+ /* @__PURE__ */ jsx54("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxs41("div", { className: "flex min-w-0 items-center gap-3", children: [
12630
+ /* @__PURE__ */ jsx54("div", { className: "min-w-0 text-[15px] font-semibold leading-5 tracking-[0] text-[var(--text-primary)]", children: translate("agentHost.agentGui.turnSummaryFilesChanged", {
12631
+ count: row.fileCount
12632
+ }) }),
12633
+ /* @__PURE__ */ jsxs41("div", { className: "inline-flex shrink-0 items-center gap-2.5 text-[11px] font-semibold", children: [
12634
+ aggregateStats.added > 0 ? /* @__PURE__ */ jsxs41("span", { className: "workspace-agents-status-panel__detail-tool-diff-added", children: [
12635
+ "+",
12636
+ aggregateStats.added
12637
+ ] }) : null,
12638
+ aggregateStats.removed > 0 ? /* @__PURE__ */ jsxs41("span", { className: "workspace-agents-status-panel__detail-tool-diff-removed", children: [
12639
+ "-",
12640
+ aggregateStats.removed
12641
+ ] }) : null
12642
+ ] })
12643
+ ] }) }),
12644
+ canRenderPatchButton ? /* @__PURE__ */ jsx54(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs41(Tooltip, { children: [
12645
+ /* @__PURE__ */ jsx54(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx54(
12646
+ "span",
12647
+ {
12648
+ className: `inline-flex shrink-0 ${isPatchActionDisabled ? "cursor-not-allowed" : ""}`,
12649
+ children: /* @__PURE__ */ jsxs41(
12650
+ CanvasNodeGhostIconButton,
12651
+ {
12652
+ "aria-label": patchActionLabel,
12653
+ className: "w-auto min-w-0 gap-1.5 px-2 text-[12px] font-medium leading-4 disabled:pointer-events-none",
12654
+ disabled: isPatchActionDisabled,
12655
+ onClick: handlePatchAction,
12656
+ children: [
12657
+ patchPending || isPatchSupportChecking ? /* @__PURE__ */ jsx54(
12658
+ LoaderCircle2,
12659
+ {
12660
+ width: 14,
12661
+ height: 14,
12662
+ "aria-hidden": "true",
12663
+ className: "animate-spin text-[var(--text-secondary)]"
12664
+ }
12665
+ ) : patchAction === "undo" ? /* @__PURE__ */ jsx54(
12666
+ Undo2,
12667
+ {
12668
+ width: 14,
12669
+ height: 14,
12670
+ "aria-hidden": "true",
12671
+ className: "text-[var(--text-secondary)]"
12672
+ }
12673
+ ) : /* @__PURE__ */ jsx54(
12674
+ Redo2,
12675
+ {
12676
+ width: 14,
12677
+ height: 14,
12678
+ "aria-hidden": "true",
12679
+ className: "text-[var(--text-secondary)]"
12680
+ }
12681
+ ),
12682
+ /* @__PURE__ */ jsx54("span", { children: patchActionLabel })
12683
+ ]
12684
+ }
12685
+ )
12686
+ }
12687
+ ) }),
12688
+ patchDisabledReason ? /* @__PURE__ */ jsx54(TooltipContent, { className: "max-w-[260px] whitespace-normal text-left", children: patchDisabledReason }) : null
12689
+ ] }) }) : null
12690
+ ] }),
11988
12691
  /* @__PURE__ */ jsxs41("div", { className: "agent-turn-summary-card__list", children: [
11989
12692
  visibleFiles.map((file) => {
11990
12693
  const key = `${file.path}:${file.messageId}`;
@@ -12215,6 +12918,47 @@ function summarizeRowDiff(files) {
12215
12918
  { added: 0, removed: 0 }
12216
12919
  );
12217
12920
  }
12921
+ function showPatchFailureToast(agentHostApi, message) {
12922
+ if (agentHostApi?.toast?.error) {
12923
+ agentHostApi.toast.error(message);
12924
+ return;
12925
+ }
12926
+ toast.error(message);
12927
+ }
12928
+ function buildExecutablePatchBatches(sourceBatches, workspaceRoot) {
12929
+ return sourceBatches.flatMap((batch) => {
12930
+ const sourceCwd = patchBatchDirectoryCwd(
12931
+ batch.cwd ?? workspaceRoot ?? null,
12932
+ batch.changes
12933
+ );
12934
+ const executionCwd = resolvePatchExecutionCwd(sourceCwd, workspaceRoot);
12935
+ const diffCwd = resolvePatchDiffCwd({
12936
+ executionCwd,
12937
+ sourceCwd,
12938
+ changes: batch.changes
12939
+ });
12940
+ const diff = buildAgentTurnSummaryPatchDiff({
12941
+ ...batch,
12942
+ cwd: diffCwd
12943
+ });
12944
+ return executionCwd && diff.trim() ? [{ cwd: executionCwd, diff }] : [];
12945
+ });
12946
+ }
12947
+ function fallbackPatchFileCwd(path, workspaceRoot) {
12948
+ const root = workspaceRoot?.trim() ?? "";
12949
+ if (root && root !== "/") {
12950
+ return root;
12951
+ }
12952
+ const normalizedPath = path.trim().replaceAll("\\", "/").replace(/\/+$/, "");
12953
+ if (!normalizedPath.startsWith("/")) {
12954
+ return root || null;
12955
+ }
12956
+ const index = normalizedPath.lastIndexOf("/");
12957
+ if (index <= 0) {
12958
+ return "/";
12959
+ }
12960
+ return normalizedPath.slice(0, index);
12961
+ }
12218
12962
  function summarizeFileDiff(file) {
12219
12963
  if (file.unifiedDiff?.trim()) {
12220
12964
  return parseAgentUnifiedDiffStats(file.unifiedDiff);
@@ -12745,6 +13489,8 @@ function useProjectedAgentConversation({
12745
13489
  }
12746
13490
 
12747
13491
  export {
13492
+ getAppErrorCode,
13493
+ toLocalShortDateTime,
12748
13494
  Button,
12749
13495
  buildWorkspaceAgentSessionDetailViewModel,
12750
13496
  projectAgentConversationVM,
@@ -12764,4 +13510,4 @@ export {
12764
13510
  AgentConversationFlow,
12765
13511
  useProjectedAgentConversation
12766
13512
  };
12767
- //# sourceMappingURL=chunk-BAQTM6VS.js.map
13513
+ //# sourceMappingURL=chunk-ZAF4IVUT.js.map