agentdev 0.1.10 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/BasicAgent-7TNMYC3X.js +13 -0
  2. package/dist/ExplorerAgent-L3ZTVQGM.js +13 -0
  3. package/dist/{chunk-72H6A6NB.js → chunk-35LBACUK.js} +8 -8
  4. package/dist/{chunk-72H6A6NB.js.map → chunk-35LBACUK.js.map} +1 -1
  5. package/dist/{chunk-NORTAQIL.js → chunk-4WK7UENZ.js} +1011 -11
  6. package/dist/chunk-4WK7UENZ.js.map +1 -0
  7. package/dist/{chunk-BAP2GCYH.js → chunk-7GTVQ55R.js} +1 -1
  8. package/dist/chunk-7GTVQ55R.js.map +1 -0
  9. package/dist/{chunk-G5ECPY4K.js → chunk-EK6KGS2M.js} +87 -8
  10. package/dist/{chunk-G5ECPY4K.js.map → chunk-EK6KGS2M.js.map} +1 -1
  11. package/dist/{chunk-REOJZCSZ.js → chunk-KE3KYZVJ.js} +21 -8
  12. package/dist/chunk-KE3KYZVJ.js.map +1 -0
  13. package/dist/{chunk-EECW6PYP.js → chunk-UL2ZBPBL.js} +60 -4
  14. package/dist/chunk-UL2ZBPBL.js.map +1 -0
  15. package/dist/{chunk-3AR3JBW6.js → chunk-XRB6MD2J.js} +5436 -883
  16. package/dist/chunk-XRB6MD2J.js.map +1 -0
  17. package/dist/cli/server.js +2 -2
  18. package/dist/cli/viewer.js +2 -2
  19. package/dist/features/mcp/templates/mcp-tool.render.js +14 -1
  20. package/dist/features/mcp/templates/mcp-tool.render.js.map +1 -1
  21. package/dist/features/shell/templates/bash.render.d.ts +1 -1
  22. package/dist/features/websearch/templates/web-fetch.render.d.ts +1 -1
  23. package/dist/index.d.ts +752 -20
  24. package/dist/index.js +17 -7
  25. package/dist/index.js.map +1 -1
  26. package/dist/{notification-NWVOS2WR.js → notification-QPH37BHW.js} +29 -6
  27. package/dist/notification-QPH37BHW.js.map +1 -0
  28. package/dist/{tools-LDR3LIJP.js → tools-OKH7SPMP.js} +2 -2
  29. package/dist/{types-CF5UsxD9.d.ts → types-NVwNUVFR.d.ts} +180 -14
  30. package/package.json +6 -13
  31. package/dist/BasicAgent-R7DYGTHF.js +0 -13
  32. package/dist/ExplorerAgent-DXY3OQ5U.js +0 -13
  33. package/dist/chunk-3AR3JBW6.js.map +0 -1
  34. package/dist/chunk-BAP2GCYH.js.map +0 -1
  35. package/dist/chunk-EECW6PYP.js.map +0 -1
  36. package/dist/chunk-NORTAQIL.js.map +0 -1
  37. package/dist/chunk-REOJZCSZ.js.map +0 -1
  38. package/dist/notification-NWVOS2WR.js.map +0 -1
  39. /package/dist/{BasicAgent-R7DYGTHF.js.map → BasicAgent-7TNMYC3X.js.map} +0 -0
  40. /package/dist/{ExplorerAgent-DXY3OQ5U.js.map → ExplorerAgent-L3ZTVQGM.js.map} +0 -0
  41. /package/dist/{tools-LDR3LIJP.js.map → tools-OKH7SPMP.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getDefaultUDSPath
3
- } from "./chunk-BAP2GCYH.js";
3
+ } from "./chunk-7GTVQ55R.js";
4
4
  import {
5
5
  __require
6
6
  } from "./chunk-BDS2QGZ5.js";
@@ -1454,6 +1454,20 @@ function generateViewerHtml(port) {
1454
1454
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
1455
1455
  }
1456
1456
 
1457
+ .feature-badge.status-removed {
1458
+ color: #fff1f2;
1459
+ background: rgba(190, 18, 60, 0.72);
1460
+ border-color: rgba(225, 29, 72, 0.75);
1461
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
1462
+ }
1463
+
1464
+ .feature-badge.status-superseded {
1465
+ color: #f5f5f4;
1466
+ background: rgba(120, 113, 108, 0.72);
1467
+ border-color: rgba(168, 162, 158, 0.75);
1468
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
1469
+ }
1470
+
1457
1471
  body[data-theme="light"] .feature-badge.status-enabled {
1458
1472
  color: #166534;
1459
1473
  background: rgba(220, 252, 231, 1);
@@ -1469,6 +1483,16 @@ function generateViewerHtml(port) {
1469
1483
  background: rgba(254, 226, 226, 1);
1470
1484
  }
1471
1485
 
1486
+ body[data-theme="light"] .feature-badge.status-removed {
1487
+ color: #881337;
1488
+ background: rgba(255, 228, 230, 1);
1489
+ }
1490
+
1491
+ body[data-theme="light"] .feature-badge.status-superseded {
1492
+ color: #57534e;
1493
+ background: rgba(231, 229, 228, 1);
1494
+ }
1495
+
1472
1496
  .feature-card-detail {
1473
1497
  display: flex;
1474
1498
  align-items: center;
@@ -2696,6 +2720,18 @@ function generateViewerHtml(port) {
2696
2720
  background: transparent !important;
2697
2721
  }
2698
2722
 
2723
+ /* \u4FEE\u590D\u884C\u53F7\u4E0D\u968F\u5185\u5BB9\u6EDA\u52A8\u7684\u95EE\u9898\uFF1A\u5C06 absolute \u6539\u4E3A sticky */
2724
+ .d2h-code-side-linenumber {
2725
+ position: sticky !important;
2726
+ left: 0 !important;
2727
+ z-index: 1 !important;
2728
+ }
2729
+ .d2h-code-linenumber {
2730
+ position: sticky !important;
2731
+ left: 0 !important;
2732
+ z-index: 1 !important;
2733
+ }
2734
+
2699
2735
  /* \u7528\u6237\u8F93\u5165\u5BB9\u5668\uFF08\u9ED8\u8BA4\u9690\u85CF\uFF09 */
2700
2736
  #user-input-container {
2701
2737
  display: none;
@@ -2713,6 +2749,30 @@ function generateViewerHtml(port) {
2713
2749
  display: flex;
2714
2750
  }
2715
2751
 
2752
+ #user-input-container.choice-input-active {
2753
+ top: 56px;
2754
+ bottom: 0;
2755
+ padding: 24px;
2756
+ align-items: center;
2757
+ background: rgba(5, 7, 12, 0.64);
2758
+ backdrop-filter: blur(5px);
2759
+ pointer-events: auto;
2760
+ }
2761
+
2762
+ body[data-theme="light"] #user-input-container.choice-input-active {
2763
+ background: rgba(18, 20, 26, 0.32);
2764
+ }
2765
+
2766
+ #user-input-container.choice-input-active.choice-collapsed {
2767
+ top: auto;
2768
+ bottom: 22px;
2769
+ padding: 0 24px;
2770
+ align-items: flex-end;
2771
+ background: transparent;
2772
+ backdrop-filter: none;
2773
+ pointer-events: none;
2774
+ }
2775
+
2716
2776
  .user-input-card {
2717
2777
  pointer-events: auto;
2718
2778
  background: var(--input-card-bg);
@@ -2801,6 +2861,232 @@ function generateViewerHtml(port) {
2801
2861
  border-color: var(--text-primary);
2802
2862
  }
2803
2863
 
2864
+ .user-choice-card {
2865
+ gap: 12px;
2866
+ padding: 18px 20px;
2867
+ border-radius: 20px;
2868
+ width: min(100%, 520px);
2869
+ max-height: min(100%, 720px);
2870
+ overflow: auto;
2871
+ box-shadow: 0 28px 70px rgba(0, 0, 0, 0.38);
2872
+ }
2873
+
2874
+ .user-choice-card:focus {
2875
+ outline: none;
2876
+ border-color: var(--input-card-border);
2877
+ }
2878
+
2879
+ .user-choice-topline {
2880
+ display: flex;
2881
+ align-items: flex-start;
2882
+ justify-content: space-between;
2883
+ gap: 16px;
2884
+ }
2885
+
2886
+ .user-choice-title {
2887
+ flex: 1;
2888
+ min-width: 0;
2889
+ color: var(--text-primary);
2890
+ font-size: 18px;
2891
+ line-height: 1.45;
2892
+ font-weight: 700;
2893
+ }
2894
+
2895
+ .user-choice-progress {
2896
+ margin-left: auto;
2897
+ color: var(--text-muted);
2898
+ font-size: 12px;
2899
+ line-height: 1.4;
2900
+ white-space: nowrap;
2901
+ padding-top: 1px;
2902
+ }
2903
+
2904
+ .user-choice-question {
2905
+ color: var(--text-secondary);
2906
+ font-size: 15px;
2907
+ line-height: 1.55;
2908
+ }
2909
+
2910
+ .user-choice-close {
2911
+ width: 30px;
2912
+ height: 30px;
2913
+ border-radius: 999px;
2914
+ border: 1px solid var(--border-color);
2915
+ background: transparent;
2916
+ color: var(--text-primary);
2917
+ cursor: pointer;
2918
+ flex-shrink: 0;
2919
+ font-size: 18px;
2920
+ line-height: 1;
2921
+ }
2922
+
2923
+ .user-choice-close:hover {
2924
+ background: var(--hover-bg);
2925
+ }
2926
+
2927
+ .user-choice-options {
2928
+ display: grid;
2929
+ gap: 8px;
2930
+ }
2931
+
2932
+ .user-choice-option {
2933
+ width: 100%;
2934
+ display: grid;
2935
+ grid-template-columns: 22px minmax(0, 1fr);
2936
+ gap: 10px;
2937
+ align-items: flex-start;
2938
+ border: 1px solid var(--border-color);
2939
+ background: transparent;
2940
+ color: var(--text-primary);
2941
+ border-radius: 12px;
2942
+ padding: 10px 12px;
2943
+ cursor: pointer;
2944
+ font-family: inherit;
2945
+ text-align: left;
2946
+ transition: border-color 0.16s ease, background 0.16s ease;
2947
+ }
2948
+
2949
+ .user-choice-option:hover,
2950
+ .user-choice-option.active {
2951
+ border-color: var(--text-secondary);
2952
+ background: var(--hover-bg);
2953
+ }
2954
+
2955
+ .user-choice-key {
2956
+ width: 20px;
2957
+ height: 20px;
2958
+ border-radius: 50%;
2959
+ border: 1px solid var(--border-color);
2960
+ color: var(--text-muted);
2961
+ display: inline-flex;
2962
+ align-items: center;
2963
+ justify-content: center;
2964
+ font-size: 11px;
2965
+ line-height: 1;
2966
+ margin-top: 1px;
2967
+ }
2968
+
2969
+ .user-choice-option.active .user-choice-key {
2970
+ color: var(--text-primary);
2971
+ border-color: var(--text-primary);
2972
+ }
2973
+
2974
+ .user-choice-label {
2975
+ font-size: 14px;
2976
+ line-height: 1.35;
2977
+ color: var(--text-primary);
2978
+ overflow-wrap: anywhere;
2979
+ }
2980
+
2981
+ .user-choice-description {
2982
+ margin-top: 3px;
2983
+ font-size: 12px;
2984
+ line-height: 1.45;
2985
+ color: var(--text-muted);
2986
+ overflow-wrap: anywhere;
2987
+ }
2988
+
2989
+ .user-choice-custom {
2990
+ display: none;
2991
+ margin-top: -2px;
2992
+ }
2993
+
2994
+ .user-choice-custom.active {
2995
+ display: block;
2996
+ }
2997
+
2998
+ .user-choice-custom textarea,
2999
+ .user-choice-supplement textarea {
3000
+ width: 100%;
3001
+ min-height: 42px;
3002
+ max-height: 140px;
3003
+ resize: none;
3004
+ box-sizing: border-box;
3005
+ border: 1px solid var(--border-color);
3006
+ background: transparent;
3007
+ color: var(--text-primary);
3008
+ border-radius: 12px;
3009
+ padding: 10px 12px;
3010
+ font: inherit;
3011
+ font-size: 14px;
3012
+ line-height: 1.45;
3013
+ outline: none;
3014
+ }
3015
+
3016
+ .user-choice-custom textarea:focus,
3017
+ .user-choice-supplement textarea:focus {
3018
+ border-color: var(--text-secondary);
3019
+ }
3020
+
3021
+ .user-choice-supplement {
3022
+ display: none;
3023
+ margin-top: -2px;
3024
+ }
3025
+
3026
+ .user-choice-supplement.active {
3027
+ display: block;
3028
+ }
3029
+
3030
+ .user-choice-supplement-label {
3031
+ font-size: 12px;
3032
+ color: var(--text-muted);
3033
+ margin-bottom: 4px;
3034
+ }
3035
+
3036
+ .user-choice-footer {
3037
+ display: flex;
3038
+ justify-content: space-between;
3039
+ align-items: center;
3040
+ gap: 12px;
3041
+ color: var(--text-muted);
3042
+ font-size: 12px;
3043
+ line-height: 1.4;
3044
+ }
3045
+
3046
+ .user-choice-submit {
3047
+ border: 1px solid var(--text-primary);
3048
+ background: var(--text-primary);
3049
+ color: var(--bg-primary);
3050
+ border-radius: 999px;
3051
+ padding: 7px 14px;
3052
+ font-size: 12px;
3053
+ cursor: pointer;
3054
+ font-family: inherit;
3055
+ }
3056
+
3057
+ .user-choice-mini {
3058
+ pointer-events: auto;
3059
+ width: min(100% - 24px, 420px);
3060
+ border: 1px solid var(--input-card-border);
3061
+ background: var(--input-card-bg);
3062
+ color: var(--text-primary);
3063
+ border-radius: 999px;
3064
+ padding: 10px 14px;
3065
+ display: flex;
3066
+ align-items: center;
3067
+ justify-content: space-between;
3068
+ gap: 12px;
3069
+ box-shadow: 0 10px 32px var(--shadow-strong);
3070
+ cursor: pointer;
3071
+ font-family: inherit;
3072
+ text-align: left;
3073
+ }
3074
+
3075
+ .user-choice-mini-title {
3076
+ min-width: 0;
3077
+ overflow: hidden;
3078
+ text-overflow: ellipsis;
3079
+ white-space: nowrap;
3080
+ font-size: 13px;
3081
+ line-height: 1.4;
3082
+ }
3083
+
3084
+ .user-choice-mini-meta {
3085
+ color: var(--text-muted);
3086
+ font-size: 12px;
3087
+ white-space: nowrap;
3088
+ }
3089
+
2804
3090
  </style>
2805
3091
  </head>
2806
3092
  <body>
@@ -2974,6 +3260,7 @@ function generateViewerHtml(port) {
2974
3260
  let allAgents = [];
2975
3261
  let currentMessages = [];
2976
3262
  let currentInputRequests = [];
3263
+ let choiceInputState = {};
2977
3264
  let toolRenderConfigs = {};
2978
3265
  let TOOL_NAMES = {};
2979
3266
  let contextMenuAgentId = null;
@@ -3036,6 +3323,7 @@ function generateViewerHtml(port) {
3036
3323
  panel_enabled: '\u5DF2\u542F\u7528',
3037
3324
  panel_partial: '\u90E8\u5206\u542F\u7528',
3038
3325
  panel_disabled: '\u5DF2\u5173\u95ED',
3326
+ panel_removed: '\u5DF2\u79FB\u9664',
3039
3327
  panel_total: '\u603B\u6570',
3040
3328
  panel_all_features: '\u5168\u90E8\u529F\u80FD\u7279\u6027',
3041
3329
  panel_registered: '\u5DF2\u6CE8\u518C',
@@ -3079,6 +3367,7 @@ function generateViewerHtml(port) {
3079
3367
  feature_enabled: 'enabled',
3080
3368
  feature_partial: 'partial',
3081
3369
  feature_disabled: 'disabled',
3370
+ feature_removed: 'removed',
3082
3371
  feature_hooks: 'hooks',
3083
3372
  feature_tools: 'tools',
3084
3373
  feature_messages: '\u6761\u6D88\u606F',
@@ -3086,9 +3375,13 @@ function generateViewerHtml(port) {
3086
3375
  feature_active_tools: '\u542F\u7528\u5DE5\u5177',
3087
3376
  feature_tool_enabled: 'enabled',
3088
3377
  feature_tool_disabled: 'disabled',
3378
+ feature_tool_removed: 'removed',
3379
+ feature_tool_superseded: '\u5DF2\u66FF\u4EE3',
3089
3380
  feature_tool_render: 'render',
3090
3381
  feature_open_details: '\u67E5\u770B\u8BE6\u60C5',
3091
3382
  feature_status_label: '\u72B6\u6001',
3383
+ standalone_tools_title: '\u76F4\u63A5\u6CE8\u518C\u7684\u5DE5\u5177',
3384
+ standalone_tools_desc: '\u975E Feature \u6CE8\u518C\u7684\u5DE5\u5177',
3092
3385
  mcp_section_kicker: 'MCP \u670D\u52A1\u5668',
3093
3386
  mcp_hero_title: 'Debugger Hub MCP \u670D\u52A1',
3094
3387
  structure_kicker: 'ReAct \u5FAA\u73AF\u62D3\u6251',
@@ -3195,6 +3488,7 @@ function generateViewerHtml(port) {
3195
3488
  panel_enabled: 'enabled',
3196
3489
  panel_partial: 'partial',
3197
3490
  panel_disabled: 'disabled',
3491
+ panel_removed: 'removed',
3198
3492
  panel_total: 'total',
3199
3493
  panel_all_features: 'All Features',
3200
3494
  panel_registered: 'registered',
@@ -3238,6 +3532,7 @@ function generateViewerHtml(port) {
3238
3532
  feature_enabled: 'enabled',
3239
3533
  feature_partial: 'partial',
3240
3534
  feature_disabled: 'disabled',
3535
+ feature_removed: 'removed',
3241
3536
  feature_hooks: 'hooks',
3242
3537
  feature_tools: 'tools',
3243
3538
  feature_messages: 'messages',
@@ -3245,9 +3540,13 @@ function generateViewerHtml(port) {
3245
3540
  feature_active_tools: 'Active Tools',
3246
3541
  feature_tool_enabled: 'enabled',
3247
3542
  feature_tool_disabled: 'disabled',
3543
+ feature_tool_removed: 'removed',
3544
+ feature_tool_superseded: 'superseded',
3248
3545
  feature_tool_render: 'render',
3249
3546
  feature_open_details: 'Open details',
3250
3547
  feature_status_label: 'Status',
3548
+ standalone_tools_title: 'Direct Registered Tools',
3549
+ standalone_tools_desc: 'Tools registered outside of Features',
3251
3550
  mcp_section_kicker: 'Model Context Protocol',
3252
3551
  mcp_hero_title: 'Debugger MCP Server',
3253
3552
  structure_kicker: 'ReAct Loop Topology',
@@ -3334,6 +3633,7 @@ function generateViewerHtml(port) {
3334
3633
  }
3335
3634
 
3336
3635
  function getFeatureStatusLabel(status) {
3636
+ if (status === 'removed') return t('feature_removed');
3337
3637
  if (status === 'disabled') return t('feature_disabled');
3338
3638
  if (status === 'partial') return t('feature_partial');
3339
3639
  return t('feature_enabled');
@@ -3555,6 +3855,7 @@ function generateViewerHtml(port) {
3555
3855
  entries: [],
3556
3856
  };
3557
3857
  }),
3858
+ standaloneTools: raw.standaloneTools || undefined,
3558
3859
  };
3559
3860
  }
3560
3861
 
@@ -4208,7 +4509,7 @@ function generateViewerHtml(port) {
4208
4509
  const status = getFeatureStatus(feature);
4209
4510
  acc[status] = (acc[status] || 0) + 1;
4210
4511
  return acc;
4211
- }, { enabled: 0, partial: 0, disabled: 0 });
4512
+ }, { enabled: 0, partial: 0, disabled: 0, removed: 0 });
4212
4513
  const selectedDoc = lifecycleDocs[selectedOverviewLifecycle] || lifecycleDocs.StepFinish;
4213
4514
  const flowChips = currentHookInspector.lifecycleOrder
4214
4515
  .map(name => '<button class="hooks-chip' + (name === selectedOverviewLifecycle ? ' active' : '') + '" type="button" onclick="window.selectOverviewLifecycle(&quot;' + escapeHtml(name) + '&quot;)"><strong>' + escapeHtml(name) + '</strong></button>')
@@ -4230,7 +4531,7 @@ function generateViewerHtml(port) {
4230
4531
  '<div class="hooks-section-header"><div class="hooks-section-title">' + escapeHtml(t('panel_inspector')) + '</div><div class="hooks-section-meta">' + escapeHtml(connected) + '</div></div>',
4231
4532
  '<div class="feature-grid">',
4232
4533
  '<div class="feature-card"><div class="feature-card-name">' + escapeHtml(t('panel_connection')) + '</div><div class="feature-card-detail"><span>' + escapeHtml(connected) + '</span><span>' + String(currentMessages.length) + ' ' + escapeHtml(t('feature_messages')) + '</span></div></div>',
4233
- '<div class="feature-card"><div class="feature-card-name">' + escapeHtml(t('panel_features_label')) + '</div><div class="feature-card-detail"><span>' + String(currentHookInspector.features.length) + ' ' + escapeHtml(t('panel_total')) + '</span><span>' + String(featureStatusCounts.enabled) + ' ' + escapeHtml(t('panel_enabled')) + '</span><span>' + String(featureStatusCounts.partial) + ' ' + escapeHtml(t('panel_partial')) + '</span><span>' + String(featureStatusCounts.disabled) + ' ' + escapeHtml(t('panel_disabled')) + '</span></div></div>',
4534
+ '<div class="feature-card"><div class="feature-card-name">' + escapeHtml(t('panel_features_label')) + '</div><div class="feature-card-detail"><span>' + String(currentHookInspector.features.length) + ' ' + escapeHtml(t('panel_total')) + '</span><span>' + String(featureStatusCounts.enabled) + ' ' + escapeHtml(t('panel_enabled')) + '</span><span>' + String(featureStatusCounts.partial) + ' ' + escapeHtml(t('panel_partial')) + '</span><span>' + String(featureStatusCounts.disabled) + ' ' + escapeHtml(t('panel_disabled')) + '</span><span>' + String(featureStatusCounts.removed) + ' ' + escapeHtml(t('panel_removed')) + '</span></div></div>',
4234
4535
  '</div>',
4235
4536
  '</section>',
4236
4537
  '<section class="hooks-section">',
@@ -4351,7 +4652,7 @@ function generateViewerHtml(port) {
4351
4652
  '<div class="feature-tool-card">',
4352
4653
  '<div class="feature-tool-top">',
4353
4654
  '<div class="feature-tool-name">' + escapeHtml(tool.name) + '</div>',
4354
- '<div class="' + getStatusBadgeClass(tool.enabled ? 'enabled' : 'disabled') + '">' + escapeHtml(tool.enabled ? t('feature_tool_enabled') : t('feature_tool_disabled')) + '</div>',
4655
+ '<div class="' + getStatusBadgeClass(tool.state || (tool.enabled ? 'enabled' : 'disabled')) + '">' + escapeHtml(tool.state === 'superseded' ? t('feature_tool_superseded') : tool.state === 'removed' ? t('feature_tool_removed') : tool.state === 'disabled' || tool.enabled === false ? t('feature_tool_disabled') : t('feature_tool_enabled')) + '</div>',
4355
4656
  '</div>',
4356
4657
  '<div class="feature-tool-desc">' + escapeHtml(tool.description || '') + '</div>',
4357
4658
  '<div class="feature-tool-meta">',
@@ -4366,12 +4667,31 @@ function generateViewerHtml(port) {
4366
4667
  '</div>',
4367
4668
  ].join('') : '';
4368
4669
 
4670
+ const standaloneSection = (currentHookInspector.standaloneTools && currentHookInspector.standaloneTools.length > 0)
4671
+ ? [
4672
+ '<section class="hooks-section">',
4673
+ '<div class="hooks-section-header"><div class="hooks-section-title">' + escapeHtml(t('standalone_tools_title')) + '</div><div class="hooks-section-meta">' + String(currentHookInspector.standaloneTools.length) + '</div></div>',
4674
+ '<div class="feature-tool-list">' + currentHookInspector.standaloneTools.map(tool => [
4675
+ '<div class="feature-tool-card">',
4676
+ '<div class="feature-tool-top">',
4677
+ '<div class="feature-tool-name">' + escapeHtml(tool.name) + '</div>',
4678
+ '<div class="' + getStatusBadgeClass(tool.state || 'enabled') + '">' + escapeHtml(tool.state === 'superseded' ? t('feature_tool_superseded') : tool.state === 'removed' ? t('feature_tool_removed') : tool.state === 'disabled' ? t('feature_tool_disabled') : t('feature_tool_enabled')) + '</div>',
4679
+ '</div>',
4680
+ '<div class="feature-tool-desc">' + escapeHtml(tool.description || '') + '</div>',
4681
+ tool.source ? '<div class="feature-tool-meta"><span class="feature-tool-pill">source: ' + escapeHtml(tool.source) + '</span></div>' : '',
4682
+ '</div>',
4683
+ ].join('')).join('') + '</div>',
4684
+ '</section>',
4685
+ ].join('')
4686
+ : '';
4687
+
4369
4688
  return [
4370
4689
  '<div class="hooks-panel feature-detail-shell">',
4371
4690
  '<section class="hooks-section">',
4372
4691
  '<div class="hooks-section-header"><div class="hooks-section-title">' + escapeHtml(t('panel_all_features')) + '</div><div class="hooks-section-meta">' + String(currentHookInspector.features.length) + ' ' + escapeHtml(t('panel_registered')) + '</div></div>',
4373
4692
  '<div class="feature-grid">' + featureCards + '</div>',
4374
4693
  '</section>',
4694
+ standaloneSection,
4375
4695
  detailOverlay,
4376
4696
  '</div>',
4377
4697
  ].join('');
@@ -5357,11 +5677,26 @@ function generateViewerHtml(port) {
5357
5677
  const container = document.getElementById('user-input-container');
5358
5678
  if (!container) return;
5359
5679
  currentInputRequests = requests;
5680
+ const hasChoiceRequest = Array.isArray(requests) && requests.some(isChoiceInputRequest);
5360
5681
 
5361
5682
  // \u6E05\u7A7A\u73B0\u6709\u5185\u5BB9
5362
5683
  container.innerHTML = '';
5684
+ container.classList.toggle('choice-input-active', hasChoiceRequest);
5685
+ container.classList.remove('choice-collapsed');
5686
+ container.onclick = hasChoiceRequest
5687
+ ? function(event) {
5688
+ if (event.target === container) {
5689
+ collapsePrimaryChoiceRequest();
5690
+ }
5691
+ }
5692
+ : null;
5363
5693
 
5364
5694
  for (const req of requests) {
5695
+ if (isChoiceInputRequest(req)) {
5696
+ renderChoiceInputRequest(container, req);
5697
+ continue;
5698
+ }
5699
+
5365
5700
  const card = document.createElement('div');
5366
5701
  card.className = 'user-input-card';
5367
5702
  const actionsHtml = Array.isArray(req.actions) && req.actions.length > 0
@@ -5398,6 +5733,339 @@ function generateViewerHtml(port) {
5398
5733
  }
5399
5734
  }
5400
5735
 
5736
+ function isChoiceInputRequest(req) {
5737
+ return !!req && req.mode === 'choices' && Array.isArray(req.questions) && req.questions.length > 0;
5738
+ }
5739
+
5740
+ function getChoiceRequestById(requestId) {
5741
+ return (currentInputRequests || []).find(req => req.requestId === requestId) || null;
5742
+ }
5743
+
5744
+ function getChoiceState(requestId) {
5745
+ if (!choiceInputState[requestId]) {
5746
+ choiceInputState[requestId] = {
5747
+ questionIndex: 0,
5748
+ answers: [],
5749
+ selectedIndex: 0,
5750
+ selectedIndexByQuestion: {},
5751
+ customTextByQuestion: {},
5752
+ supplementTextByOption: {},
5753
+ collapsed: false,
5754
+ };
5755
+ }
5756
+ return choiceInputState[requestId];
5757
+ }
5758
+
5759
+ function getChoiceOptionCount(question) {
5760
+ const optionCount = Array.isArray(question?.options) ? Math.min(question.options.length, 4) : 0;
5761
+ return optionCount + (question?.allowCustom ? 1 : 0);
5762
+ }
5763
+
5764
+ function buildChoiceAnswer(req, state, questionIndex) {
5765
+ const question = req?.questions?.[questionIndex] || {};
5766
+ const options = Array.isArray(question.options) ? question.options.slice(0, 4) : [];
5767
+ const selectedIndex = state.selectedIndexByQuestion?.[question.id] ?? (questionIndex === state.questionIndex ? state.selectedIndex : 0);
5768
+ const isCustom = question.allowCustom && selectedIndex >= options.length;
5769
+ if (isCustom) {
5770
+ return {
5771
+ questionId: question.id,
5772
+ customText: (state.customTextByQuestion[question.id] || '').trim(),
5773
+ };
5774
+ }
5775
+ const selectedOption = options[selectedIndex];
5776
+ const supplementKey = question.id + ':' + (selectedOption?.id || '');
5777
+ const supplementText = selectedOption?.allowSupplement
5778
+ ? (state.supplementTextByOption?.[supplementKey] || '').trim()
5779
+ : undefined;
5780
+ return {
5781
+ questionId: question.id,
5782
+ optionId: selectedOption?.id,
5783
+ supplementText: supplementText || undefined,
5784
+ };
5785
+ }
5786
+
5787
+ function rememberCurrentChoice(req, state) {
5788
+ const question = req?.questions?.[state.questionIndex] || {};
5789
+ if (!question.id) return;
5790
+ state.selectedIndexByQuestion[question.id] = state.selectedIndex || 0;
5791
+ state.answers[state.questionIndex] = buildChoiceAnswer(req, state, state.questionIndex);
5792
+ }
5793
+
5794
+ function renderChoiceInputRequest(container, req) {
5795
+ const state = getChoiceState(req.requestId);
5796
+ const questions = Array.isArray(req.questions) ? req.questions : [];
5797
+ if (state.collapsed) {
5798
+ container.classList.add('choice-collapsed');
5799
+ const mini = document.createElement('button');
5800
+ mini.className = 'user-choice-mini';
5801
+ mini.type = 'button';
5802
+ mini.setAttribute('onclick', \`expandChoiceRequest('\${req.requestId}')\`);
5803
+ mini.innerHTML = \`
5804
+ <span class="user-choice-mini-title">\${escapeHtml(req.prompt || '\u7B49\u5F85\u4F60\u7684\u9009\u62E9')}</span>
5805
+ <span class="user-choice-mini-meta">\${Math.min((state.questionIndex || 0) + 1, questions.length)} / \${questions.length}</span>
5806
+ \`;
5807
+ container.appendChild(mini);
5808
+ return;
5809
+ }
5810
+
5811
+ container.classList.remove('choice-collapsed');
5812
+ const questionIndex = Math.max(0, Math.min(state.questionIndex || 0, questions.length - 1));
5813
+ state.questionIndex = questionIndex;
5814
+ const question = questions[questionIndex] || {};
5815
+ const options = Array.isArray(question.options) ? question.options.slice(0, 4) : [];
5816
+ const hasCustom = !!question.allowCustom;
5817
+ const optionCount = options.length + (hasCustom ? 1 : 0);
5818
+ state.selectedIndex = Math.max(0, Math.min(state.selectedIndexByQuestion?.[question.id] ?? state.selectedIndex ?? 0, Math.max(0, optionCount - 1)));
5819
+
5820
+ const card = document.createElement('div');
5821
+ card.className = 'user-input-card user-choice-card';
5822
+ card.tabIndex = 0;
5823
+ card.setAttribute('onkeydown', \`handleChoiceKey(event, '\${req.requestId}')\`);
5824
+
5825
+ const optionHtml = options.map((option, index) => {
5826
+ const isActive = index === state.selectedIndex;
5827
+ const supplementKey = question.id + ':' + (option.id || '');
5828
+ const supplementText = state.supplementTextByOption?.[supplementKey] || '';
5829
+ const showSupplement = isActive && option.allowSupplement;
5830
+ const supplementLabel = option.supplementLabel || '';
5831
+ const supplementPlaceholder = option.supplementPlaceholder || '';
5832
+ return \`
5833
+ <div>
5834
+ <button class="user-choice-option \${isActive ? 'active' : ''}" type="button" onclick="selectChoiceOption('\${req.requestId}', \${index})">
5835
+ <span class="user-choice-key">\${index + 1}</span>
5836
+ <span>
5837
+ <span class="user-choice-label">\${escapeHtml(option.label || option.id || ('\u9009\u9879 ' + (index + 1)))}</span>
5838
+ \${option.description ? \`<span class="user-choice-description">\${escapeHtml(option.description)}</span>\` : ''}
5839
+ </span>
5840
+ </button>
5841
+ <div class="user-choice-supplement \${showSupplement ? 'active' : ''}">
5842
+ \${supplementLabel ? \`<div class="user-choice-supplement-label">\${escapeHtml(supplementLabel)}</div>\` : ''}
5843
+ <textarea id="choice-supplement-\${req.requestId}-\${index}" rows="2"
5844
+ oninput="updateChoiceSupplementText('\${req.requestId}', '\${supplementKey}', this.value); autoResize(this)"
5845
+ onkeydown="handleChoiceCustomKey(event, '\${req.requestId}')"
5846
+ placeholder="\${escapeHtml(supplementPlaceholder || '\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09')}">\${escapeHtml(supplementText)}</textarea>
5847
+ </div>
5848
+ </div>
5849
+ \`;
5850
+ }).join('');
5851
+
5852
+ const customIndex = options.length;
5853
+ const customActive = hasCustom && state.selectedIndex === customIndex;
5854
+ const customText = state.customTextByQuestion[question.id] || '';
5855
+ const customHtml = hasCustom ? \`
5856
+ <button class="user-choice-option \${customActive ? 'active' : ''}" type="button" onclick="selectChoiceOption('\${req.requestId}', \${customIndex})">
5857
+ <span class="user-choice-key">\${customIndex + 1}</span>
5858
+ <span>
5859
+ <span class="user-choice-label">\${escapeHtml(question.customLabel || '\u5176\u4ED6\uFF0C\u6211\u60F3\u8865\u5145')}</span>
5860
+ <span class="user-choice-description">\u9009\u62E9\u540E\u53EF\u4EE5\u76F4\u63A5\u8F93\u5165\u60F3\u8BF4\u7684\u8BDD</span>
5861
+ </span>
5862
+ </button>
5863
+ <div class="user-choice-custom \${customActive ? 'active' : ''}">
5864
+ <textarea id="choice-custom-\${req.requestId}" rows="2"
5865
+ oninput="updateChoiceCustomText('\${req.requestId}', this.value); autoResize(this)"
5866
+ onkeydown="handleChoiceCustomKey(event, '\${req.requestId}')"
5867
+ placeholder="\${escapeHtml(question.customPlaceholder || '\u8F93\u5165\u4F60\u7684\u8865\u5145\u5185\u5BB9')}">\${escapeHtml(customText)}</textarea>
5868
+ </div>
5869
+ \` : '';
5870
+
5871
+ card.innerHTML = \`
5872
+ <div class="user-choice-topline">
5873
+ <div class="user-choice-title">\${escapeHtml(req.prompt || '\u9700\u8981\u4F60\u505A\u4E2A\u9009\u62E9')}</div>
5874
+ <div class="user-choice-progress">\${questionIndex + 1} / \${questions.length}</div>
5875
+ <button class="user-choice-close" type="button" title="\u4E34\u65F6\u6536\u8D77" onclick="collapseChoiceRequest('\${req.requestId}')">\xD7</button>
5876
+ </div>
5877
+ <div class="user-choice-question">\${escapeHtml(question.question || '')}</div>
5878
+ <div class="user-choice-options">
5879
+ \${optionHtml}
5880
+ \${customHtml}
5881
+ </div>
5882
+ <div class="user-choice-footer">
5883
+ <span>\u2191\u2193 \u9009\u9879\uFF0C\u2190\u2192 \u9898\u76EE\uFF0CEnter \u786E\u8BA4</span>
5884
+ <button class="user-choice-submit" type="button" onclick="confirmChoiceQuestion('\${req.requestId}')">\${questionIndex + 1 === questions.length ? '\u63D0\u4EA4' : '\u4E0B\u4E00\u9898'}</button>
5885
+ </div>
5886
+ \`;
5887
+
5888
+ container.appendChild(card);
5889
+ setTimeout(() => {
5890
+ const customInput = customActive ? document.getElementById(\`choice-custom-\${req.requestId}\`) : null;
5891
+ const activeOption = options[state.selectedIndex];
5892
+ const supplementInput = !customActive && activeOption?.allowSupplement
5893
+ ? document.getElementById('choice-supplement-' + req.requestId + '-' + state.selectedIndex)
5894
+ : null;
5895
+ const target = customInput || supplementInput || card;
5896
+ target.focus();
5897
+ if (customInput || supplementInput) {
5898
+ const el = customInput || supplementInput;
5899
+ const end = el.value.length;
5900
+ el.setSelectionRange(end, end);
5901
+ autoResize(el);
5902
+ }
5903
+ }, 30);
5904
+ }
5905
+
5906
+ function rerenderChoiceRequest(requestId) {
5907
+ renderInputRequests(currentInputRequests || []);
5908
+ }
5909
+
5910
+ window.selectChoiceOption = function(requestId, optionIndex) {
5911
+ const req = getChoiceRequestById(requestId);
5912
+ const state = getChoiceState(requestId);
5913
+ state.selectedIndex = optionIndex;
5914
+ const question = req?.questions?.[state.questionIndex];
5915
+ if (question?.id) {
5916
+ state.selectedIndexByQuestion[question.id] = optionIndex;
5917
+ }
5918
+ rerenderChoiceRequest(requestId);
5919
+ };
5920
+
5921
+ window.collapseChoiceRequest = function(requestId) {
5922
+ const state = getChoiceState(requestId);
5923
+ const req = getChoiceRequestById(requestId);
5924
+ rememberCurrentChoice(req, state);
5925
+ state.collapsed = true;
5926
+ rerenderChoiceRequest(requestId);
5927
+ };
5928
+
5929
+ window.expandChoiceRequest = function(requestId) {
5930
+ const state = getChoiceState(requestId);
5931
+ state.collapsed = false;
5932
+ rerenderChoiceRequest(requestId);
5933
+ };
5934
+
5935
+ function collapsePrimaryChoiceRequest() {
5936
+ const request = (currentInputRequests || []).find(isChoiceInputRequest);
5937
+ if (request) {
5938
+ window.collapseChoiceRequest(request.requestId);
5939
+ }
5940
+ }
5941
+
5942
+ window.updateChoiceCustomText = function(requestId, value) {
5943
+ const req = getChoiceRequestById(requestId);
5944
+ const state = getChoiceState(requestId);
5945
+ const question = req?.questions?.[state.questionIndex];
5946
+ if (question?.id) {
5947
+ state.customTextByQuestion[question.id] = value;
5948
+ }
5949
+ };
5950
+
5951
+ window.updateChoiceSupplementText = function(requestId, supplementKey, value) {
5952
+ const state = getChoiceState(requestId);
5953
+ if (!state.supplementTextByOption) {
5954
+ state.supplementTextByOption = {};
5955
+ }
5956
+ state.supplementTextByOption[supplementKey] = value;
5957
+ };
5958
+
5959
+ window.handleChoiceKey = function(event, requestId) {
5960
+ const req = getChoiceRequestById(requestId);
5961
+ if (!req) return;
5962
+ const state = getChoiceState(requestId);
5963
+ const question = req.questions[state.questionIndex] || {};
5964
+ const optionCount = getChoiceOptionCount(question);
5965
+ if (event.key === 'ArrowDown') {
5966
+ event.preventDefault();
5967
+ state.selectedIndex = Math.min(optionCount - 1, (state.selectedIndex || 0) + 1);
5968
+ if (question.id) state.selectedIndexByQuestion[question.id] = state.selectedIndex;
5969
+ rerenderChoiceRequest(requestId);
5970
+ } else if (event.key === 'ArrowUp') {
5971
+ event.preventDefault();
5972
+ state.selectedIndex = Math.max(0, (state.selectedIndex || 0) - 1);
5973
+ if (question.id) state.selectedIndexByQuestion[question.id] = state.selectedIndex;
5974
+ rerenderChoiceRequest(requestId);
5975
+ } else if (event.key === 'ArrowRight') {
5976
+ event.preventDefault();
5977
+ rememberCurrentChoice(req, state);
5978
+ state.questionIndex = Math.min(req.questions.length - 1, state.questionIndex + 1);
5979
+ state.selectedIndex = state.selectedIndexByQuestion[req.questions[state.questionIndex]?.id] ?? 0;
5980
+ rerenderChoiceRequest(requestId);
5981
+ } else if (event.key === 'ArrowLeft') {
5982
+ event.preventDefault();
5983
+ rememberCurrentChoice(req, state);
5984
+ state.questionIndex = Math.max(0, state.questionIndex - 1);
5985
+ state.selectedIndex = state.selectedIndexByQuestion[req.questions[state.questionIndex]?.id] ?? 0;
5986
+ rerenderChoiceRequest(requestId);
5987
+ } else if (event.key === 'Enter') {
5988
+ event.preventDefault();
5989
+ confirmChoiceQuestion(requestId);
5990
+ }
5991
+ };
5992
+
5993
+ window.handleChoiceCustomKey = function(event, requestId) {
5994
+ if (event.key === 'Enter' && !event.shiftKey && !event.ctrlKey) {
5995
+ event.preventDefault();
5996
+ confirmChoiceQuestion(requestId);
5997
+ }
5998
+ };
5999
+
6000
+ window.confirmChoiceQuestion = async function(requestId) {
6001
+ const req = getChoiceRequestById(requestId);
6002
+ if (!req) return;
6003
+ const state = getChoiceState(requestId);
6004
+ const questions = req.questions || [];
6005
+ rememberCurrentChoice(req, state);
6006
+
6007
+ // Validate supplement-required for current question
6008
+ const currentQuestion = questions[state.questionIndex];
6009
+ if (currentQuestion) {
6010
+ const currentAnswer = state.answers[state.questionIndex];
6011
+ if (currentAnswer && currentAnswer.optionId) {
6012
+ const selectedOption = (currentQuestion.options || []).find(o => o.id === currentAnswer.optionId);
6013
+ if (selectedOption && selectedOption.allowSupplement && selectedOption.supplementRequired) {
6014
+ const supplementKey = currentQuestion.id + ':' + currentAnswer.optionId;
6015
+ const supplementValue = (state.supplementTextByOption?.[supplementKey] || '').trim();
6016
+ if (!supplementValue) {
6017
+ const supplementInput = document.getElementById('choice-supplement-' + requestId + '-' + (currentQuestion.options.indexOf(selectedOption)));
6018
+ if (supplementInput) {
6019
+ supplementInput.focus();
6020
+ supplementInput.style.borderColor = '#dc3545';
6021
+ setTimeout(() => { supplementInput.style.borderColor = ''; }, 1500);
6022
+ }
6023
+ return;
6024
+ }
6025
+ }
6026
+ }
6027
+ }
6028
+
6029
+ if (state.questionIndex < questions.length - 1) {
6030
+ state.questionIndex += 1;
6031
+ state.selectedIndex = state.selectedIndexByQuestion[questions[state.questionIndex]?.id] ?? 0;
6032
+ rerenderChoiceRequest(requestId);
6033
+ return;
6034
+ }
6035
+
6036
+ const finalAnswers = questions.map((_, index) => state.answers[index] || buildChoiceAnswer(req, state, index));
6037
+ const summary = finalAnswers.map((item, index) => {
6038
+ const q = questions[index] || {};
6039
+ if (item.customText) return \`\${q.question || item.questionId}: \${item.customText}\`;
6040
+ const option = (q.options || []).find(candidate => candidate.id === item.optionId);
6041
+ let line = \`\${q.question || item.questionId}: \${option?.label || item.optionId || ''}\`;
6042
+ if (item.supplementText) line += \` (\${item.supplementText})\`;
6043
+ return line;
6044
+ }).join('\\n');
6045
+
6046
+ try {
6047
+ const res = await fetch(\`/api/agents/\${currentAgentId}/input\`, {
6048
+ method: 'POST',
6049
+ headers: { 'Content-Type': 'application/json' },
6050
+ body: JSON.stringify({
6051
+ requestId,
6052
+ input: summary,
6053
+ response: {
6054
+ kind: 'choices',
6055
+ choices: finalAnswers,
6056
+ text: summary,
6057
+ },
6058
+ }),
6059
+ });
6060
+ if (res.ok) {
6061
+ delete choiceInputState[requestId];
6062
+ poll();
6063
+ }
6064
+ } catch (e) {
6065
+ console.error('\u63D0\u4EA4\u9009\u62E9\u5931\u8D25:', e);
6066
+ }
6067
+ };
6068
+
5401
6069
  function syncRollbackActionButtons() {
5402
6070
  const allowRollback = !!getPrimaryInputRequest();
5403
6071
  const rows = container.querySelectorAll('.message-row');
@@ -5623,7 +6291,7 @@ function generateViewerHtml(port) {
5623
6291
  </div>
5624
6292
  </div>
5625
6293
  \`;
5626
- } else if (msg.content.startsWith('[Error:')) {
6294
+ } else if (msg.content.startsWith('[Error:') || msg.content.startsWith('[API Error:')) {
5627
6295
  // \u9519\u8BEF\u6D88\u606F\u4F7F\u7528\u7EA2\u8272\u6837\u5F0F
5628
6296
  innerContent += \`<div class="tool-error">\${escapeHtml(msg.content)}</div>\`;
5629
6297
  } else {
@@ -6278,6 +6946,9 @@ var ViewerWorker = class {
6278
6946
  case "request-input":
6279
6947
  this.handleRequestInput(msg);
6280
6948
  break;
6949
+ case "consume-queued-input":
6950
+ this.handleConsumeQueuedInput(msg.agentId, msg.inputId);
6951
+ break;
6281
6952
  case "stop":
6282
6953
  this.handleStop();
6283
6954
  break;
@@ -6405,6 +7076,31 @@ var ViewerWorker = class {
6405
7076
  this.handlePostInput(req, res, inputPostMatch[1]);
6406
7077
  return;
6407
7078
  }
7079
+ const queueInputMatch = url.match(/^\/api\/agents\/([^/]+)\/queue-input$/);
7080
+ if (queueInputMatch && req.method === "POST") {
7081
+ this.handleQueueInput(req, res, queueInputMatch[1]);
7082
+ return;
7083
+ }
7084
+ const queuedInputsMatch = url.match(/^\/api\/agents\/([^/]+)\/queued-inputs$/);
7085
+ if (queuedInputsMatch && req.method === "GET") {
7086
+ this.handleGetQueuedInputs(req, res, queuedInputsMatch[1]);
7087
+ return;
7088
+ }
7089
+ const dequeueMatch = url.match(/^\/api\/agents\/([^/]+)\/dequeue-input$/);
7090
+ if (dequeueMatch && req.method === "POST") {
7091
+ this.handleDequeueInput(req, res, dequeueMatch[1]);
7092
+ return;
7093
+ }
7094
+ const interruptMatch = url.match(/^\/api\/agents\/([^/]+)\/interrupt$/);
7095
+ if (interruptMatch && req.method === "POST") {
7096
+ this.handleInterrupt(req, res, interruptMatch[1]);
7097
+ return;
7098
+ }
7099
+ const runningMatch = url.match(/^\/api\/agents\/([^/]+)\/running$/);
7100
+ if (runningMatch && req.method === "GET") {
7101
+ this.handleGetRunning(req, res, runningMatch[1]);
7102
+ return;
7103
+ }
6408
7104
  if (url === "/api/messages" && req.method === "GET") {
6409
7105
  if (this.currentAgentId) {
6410
7106
  this.handleGetAgentMessages(req, res, this.currentAgentId);
@@ -6684,7 +7380,7 @@ var ViewerWorker = class {
6684
7380
  return;
6685
7381
  }
6686
7382
  res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
6687
- res.end(JSON.stringify(session.overview || this.createEmptyOverview()));
7383
+ res.end(JSON.stringify(this.getMergedOverview(session)));
6688
7384
  }
6689
7385
  /**
6690
7386
  * GET /api/agents/:id/notification - 获取指定 Agent 的通知状态
@@ -6701,6 +7397,9 @@ var ViewerWorker = class {
6701
7397
  res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
6702
7398
  res.end(JSON.stringify({
6703
7399
  state: session.currentState,
7400
+ event: session.events.length > 0 ? session.events[session.events.length - 1] : null,
7401
+ runtime: this.cloneRuntimeState(this.getSessionRuntimeState(session)),
7402
+ callActive: session.callActive === true,
6704
7403
  hasNewEvents
6705
7404
  }));
6706
7405
  }
@@ -6819,6 +7518,8 @@ var ViewerWorker = class {
6819
7518
  placeholder: data.placeholder,
6820
7519
  initialValue: data.initialValue,
6821
7520
  actions: data.actions,
7521
+ mode: data.mode,
7522
+ questions: data.questions,
6822
7523
  timestamp: data.timestamp
6823
7524
  }));
6824
7525
  res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
@@ -6891,6 +7592,173 @@ var ViewerWorker = class {
6891
7592
  }
6892
7593
  });
6893
7594
  }
7595
+ /**
7596
+ * 排队用户输入(运行期间提交的消息)
7597
+ */
7598
+ handleQueueInput(req, res, agentId) {
7599
+ let body = "";
7600
+ req.on("data", (chunk) => {
7601
+ body += chunk;
7602
+ });
7603
+ req.on("end", () => {
7604
+ try {
7605
+ const { text } = JSON.parse(body);
7606
+ if (!text || typeof text !== "string") {
7607
+ res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
7608
+ res.end(JSON.stringify({ error: "Missing or invalid text" }));
7609
+ return;
7610
+ }
7611
+ const session = this.agentSessions.get(agentId);
7612
+ if (!session) {
7613
+ res.writeHead(404, { "Content-Type": "application/json; charset=utf-8" });
7614
+ res.end(JSON.stringify({ error: "Agent not found" }));
7615
+ return;
7616
+ }
7617
+ if (!session.queuedInputs) {
7618
+ session.queuedInputs = [];
7619
+ }
7620
+ const queuedInput = {
7621
+ id: `q-${Date.now()}-${Math.random().toString(36).slice(2)}`,
7622
+ text,
7623
+ timestamp: Date.now()
7624
+ };
7625
+ session.queuedInputs.push(queuedInput);
7626
+ console.log(`[Viewer Worker] \u7528\u6237\u8F93\u5165\u5DF2\u6392\u961F: ${agentId}, queueLength=${session.queuedInputs.length}`);
7627
+ const targetClientId = session.clientId;
7628
+ const targetSocket = targetClientId ? this.udsClients.get(targetClientId) : null;
7629
+ session.useArbiterQueue = !!targetSocket;
7630
+ if (targetSocket) {
7631
+ try {
7632
+ targetSocket.write(JSON.stringify({
7633
+ type: "queue-input",
7634
+ agentId,
7635
+ input: queuedInput
7636
+ }) + "\n");
7637
+ } catch (writeError) {
7638
+ console.error("[Viewer Worker] queue-input \u8F6C\u53D1\u5230\u8FD0\u884C\u65F6\u5931\u8D25:", writeError);
7639
+ }
7640
+ }
7641
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
7642
+ res.end(JSON.stringify({ success: true, id: queuedInput.id, queueLength: session.queuedInputs.length }));
7643
+ } catch (e) {
7644
+ res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
7645
+ res.end(JSON.stringify({ error: "Invalid request" }));
7646
+ }
7647
+ });
7648
+ }
7649
+ /**
7650
+ * 获取排队中的用户输入
7651
+ */
7652
+ handleGetQueuedInputs(req, res, agentId) {
7653
+ const session = this.agentSessions.get(agentId);
7654
+ if (!session) {
7655
+ res.writeHead(404, { "Content-Type": "application/json; charset=utf-8" });
7656
+ res.end(JSON.stringify({ error: "Agent not found" }));
7657
+ return;
7658
+ }
7659
+ const queued = session.queuedInputs || [];
7660
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
7661
+ res.end(JSON.stringify(queued));
7662
+ }
7663
+ /**
7664
+ * 消费第一条排队消息
7665
+ */
7666
+ handleDequeueInput(req, res, agentId) {
7667
+ const session = this.agentSessions.get(agentId);
7668
+ if (!session) {
7669
+ res.writeHead(404, { "Content-Type": "application/json; charset=utf-8" });
7670
+ res.end(JSON.stringify({ error: "Agent not found" }));
7671
+ return;
7672
+ }
7673
+ if (session.useArbiterQueue) {
7674
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
7675
+ res.end(JSON.stringify({ input: null, remaining: session.queuedInputs?.length || 0 }));
7676
+ return;
7677
+ }
7678
+ const queued = session.queuedInputs || [];
7679
+ if (queued.length === 0) {
7680
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
7681
+ res.end(JSON.stringify({ input: null }));
7682
+ return;
7683
+ }
7684
+ const input = queued.shift();
7685
+ console.log(`[Viewer Worker] \u6D88\u8D39\u6392\u961F\u8F93\u5165: ${agentId}, remaining=${queued.length}`);
7686
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
7687
+ res.end(JSON.stringify({ input, remaining: queued.length }));
7688
+ }
7689
+ handleConsumeQueuedInput(agentId, inputId) {
7690
+ const session = this.agentSessions.get(agentId);
7691
+ if (!session || !Array.isArray(session.queuedInputs) || !inputId) {
7692
+ return;
7693
+ }
7694
+ const nextQueue = session.queuedInputs.filter((item) => item?.id !== inputId);
7695
+ if (nextQueue.length !== session.queuedInputs.length) {
7696
+ session.queuedInputs = nextQueue;
7697
+ console.log(`[Viewer Worker] \u5DF2\u79FB\u9664\u6392\u961F\u8F93\u5165: ${agentId}, inputId=${inputId}, remaining=${session.queuedInputs.length}`);
7698
+ }
7699
+ }
7700
+ /**
7701
+ * 中断正在运行的 Agent
7702
+ */
7703
+ handleInterrupt(req, res, agentId) {
7704
+ const session = this.agentSessions.get(agentId);
7705
+ console.log(`[VW.handleInterrupt] agentId=${agentId}, sessionFound=${!!session}, sessionKeys=[${[...this.agentSessions.keys()].join(",")}]`);
7706
+ if (!session) {
7707
+ console.log(`[VW.handleInterrupt] 404 - agent not found in sessions`);
7708
+ res.writeHead(404, { "Content-Type": "application/json; charset=utf-8" });
7709
+ res.end(JSON.stringify({ error: "Agent not found" }));
7710
+ return;
7711
+ }
7712
+ const targetClientId = session.clientId;
7713
+ console.log(`[VW.handleInterrupt] session.clientId=${targetClientId}, udsClientKeys=[${[...this.udsClients.keys()].join(",")}]`);
7714
+ if (targetClientId) {
7715
+ const targetSocket = this.udsClients.get(targetClientId);
7716
+ console.log(`[VW.handleInterrupt] socketFound=${!!targetSocket}, socketDestroyed=${targetSocket?.destroyed}`);
7717
+ if (targetSocket) {
7718
+ try {
7719
+ if (Array.isArray(session.queuedInputs) && session.queuedInputs.length > 0) {
7720
+ session.queuedInputs = [];
7721
+ }
7722
+ targetSocket.write(JSON.stringify({
7723
+ type: "interrupt-agent",
7724
+ agentId,
7725
+ clearQueue: true
7726
+ }) + "\n");
7727
+ console.log(`[VW.handleInterrupt] UDS message sent to ${targetClientId}: ${agentId}`);
7728
+ } catch (writeError) {
7729
+ console.error("[VW.handleInterrupt] UDS write failed:", writeError);
7730
+ }
7731
+ } else {
7732
+ console.warn(`[VW.handleInterrupt] no UDS socket for clientId=${targetClientId}`);
7733
+ }
7734
+ } else {
7735
+ console.warn(`[VW.handleInterrupt] session has no clientId`);
7736
+ }
7737
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
7738
+ res.end(JSON.stringify({ success: true }));
7739
+ }
7740
+ /**
7741
+ * 查询 Agent 是否正在运行
7742
+ */
7743
+ handleGetRunning(req, res, agentId) {
7744
+ const session = this.agentSessions.get(agentId);
7745
+ if (!session) {
7746
+ res.writeHead(404, { "Content-Type": "application/json; charset=utf-8" });
7747
+ res.end(JSON.stringify({ error: "Agent not found" }));
7748
+ return;
7749
+ }
7750
+ const targetClientId = session.clientId;
7751
+ if (targetClientId) {
7752
+ const targetSocket = this.udsClients.get(targetClientId);
7753
+ if (targetSocket) {
7754
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
7755
+ res.end(JSON.stringify({ running: true }));
7756
+ return;
7757
+ }
7758
+ }
7759
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
7760
+ res.end(JSON.stringify({ running: false }));
7761
+ }
6894
7762
  // ========== 会话管理 ==========
6895
7763
  /**
6896
7764
  * 获取或创建会话
@@ -6907,10 +7775,13 @@ var ViewerWorker = class {
6907
7775
  lastActive: Date.now(),
6908
7776
  // 通知系统扩展
6909
7777
  currentState: null,
7778
+ callActive: false,
7779
+ runtimeState: this.createEmptyRuntimeState(),
6910
7780
  events: [],
6911
7781
  lastEventCount: 0,
6912
7782
  logs: [],
6913
- overview: this.createEmptyOverview()
7783
+ overview: this.createEmptyOverview(),
7784
+ queuedInputs: []
6914
7785
  };
6915
7786
  this.agentSessions.set(agentId, session);
6916
7787
  }
@@ -6998,7 +7869,10 @@ var ViewerWorker = class {
6998
7869
  const { agentId, overview } = msg;
6999
7870
  const session = this.agentSessions.get(agentId);
7000
7871
  if (!session) return;
7001
- session.overview = overview;
7872
+ session.overview = {
7873
+ ...overview,
7874
+ runtime: this.cloneRuntimeState(session.runtimeState || overview.runtime || this.createEmptyRuntimeState())
7875
+ };
7002
7876
  this.updateSessionActivity(agentId);
7003
7877
  }
7004
7878
  /**
@@ -7042,7 +7916,60 @@ var ViewerWorker = class {
7042
7916
  calls: [],
7043
7917
  totalRequests: 0,
7044
7918
  totalCacheHitRequests: 0
7045
- }
7919
+ },
7920
+ runtime: this.createEmptyRuntimeState()
7921
+ };
7922
+ }
7923
+ createEmptyRuntimeState() {
7924
+ return {
7925
+ stage: "idle",
7926
+ callActive: false,
7927
+ charCount: 0,
7928
+ thinkingChars: 0,
7929
+ contentChars: 0,
7930
+ toolCallCount: 0,
7931
+ activeToolNames: [],
7932
+ activeToolCount: 0,
7933
+ callStartedAt: 0,
7934
+ stageStartedAt: 0,
7935
+ updatedAt: 0,
7936
+ lastErrorType: null,
7937
+ lastErrorMessage: null
7938
+ };
7939
+ }
7940
+ cloneRuntimeState(snapshot) {
7941
+ const source = snapshot || this.createEmptyRuntimeState();
7942
+ return {
7943
+ ...source,
7944
+ activeToolNames: Array.isArray(source.activeToolNames) ? source.activeToolNames.slice() : []
7945
+ };
7946
+ }
7947
+ getRuntimeStageFromLLMPhase(phase) {
7948
+ if (phase === "thinking") return "llm_thinking";
7949
+ if (phase === "content") return "llm_content";
7950
+ if (phase === "tool_calling") return "llm_tool_call_building";
7951
+ return "awaiting_runtime";
7952
+ }
7953
+ updateRuntimeStage(runtimeState, nextStage, timestamp) {
7954
+ const nextTimestamp = timestamp || Date.now();
7955
+ return {
7956
+ ...runtimeState,
7957
+ stage: nextStage,
7958
+ stageStartedAt: runtimeState.stage === nextStage ? runtimeState.stageStartedAt || nextTimestamp : nextTimestamp,
7959
+ updatedAt: nextTimestamp
7960
+ };
7961
+ }
7962
+ getSessionRuntimeState(session) {
7963
+ if (!session.runtimeState) {
7964
+ session.runtimeState = this.createEmptyRuntimeState();
7965
+ }
7966
+ return session.runtimeState;
7967
+ }
7968
+ getMergedOverview(session) {
7969
+ const base = session.overview || this.createEmptyOverview();
7970
+ return {
7971
+ ...base,
7972
+ runtime: this.cloneRuntimeState(this.getSessionRuntimeState(session))
7046
7973
  };
7047
7974
  }
7048
7975
  /**
@@ -7142,7 +8069,9 @@ var ViewerWorker = class {
7142
8069
  handlePushNotification(msg) {
7143
8070
  const { agentId, notification } = msg;
7144
8071
  const session = this.agentSessions.get(agentId);
7145
- if (!session) return;
8072
+ if (!session) {
8073
+ return;
8074
+ }
7146
8075
  this.updateSessionActivity(agentId);
7147
8076
  if (notification?.type === "log.entry" && notification?.data) {
7148
8077
  session.logs.push(this.normalizeLogEntry(notification.data, session));
@@ -7151,6 +8080,75 @@ var ViewerWorker = class {
7151
8080
  }
7152
8081
  return;
7153
8082
  }
8083
+ const runtimeState = this.getSessionRuntimeState(session);
8084
+ if (notification.type === "call.start") {
8085
+ session.callActive = true;
8086
+ const nextStage = runtimeState.activeToolCount > 0 ? "tool_executing" : "awaiting_runtime";
8087
+ session.runtimeState = {
8088
+ ...this.updateRuntimeStage(runtimeState, nextStage, notification.timestamp || Date.now()),
8089
+ callActive: true,
8090
+ callStartedAt: runtimeState.callActive === true ? runtimeState.callStartedAt || notification.timestamp || Date.now() : notification.timestamp || Date.now(),
8091
+ lastErrorType: null,
8092
+ lastErrorMessage: null
8093
+ };
8094
+ } else if (notification.type === "call.finish") {
8095
+ const finishData = notification.data && typeof notification.data === "object" ? notification.data : {};
8096
+ const completed = finishData.completed !== false;
8097
+ session.callActive = false;
8098
+ session.runtimeState = {
8099
+ ...this.updateRuntimeStage(runtimeState, completed ? "completed" : "failed", notification.timestamp || Date.now()),
8100
+ callActive: false,
8101
+ activeToolNames: [],
8102
+ activeToolCount: 0,
8103
+ retryAttempt: void 0,
8104
+ maxRetries: void 0,
8105
+ nextRetryDelayMs: void 0
8106
+ };
8107
+ } else if (notification.type === "llm.char_count") {
8108
+ const data = notification.data && typeof notification.data === "object" ? notification.data : {};
8109
+ const charCount = typeof data.charCount === "number" ? data.charCount : runtimeState.charCount;
8110
+ const phase = typeof data.phase === "string" ? data.phase : "";
8111
+ const toolCallCount = typeof data.toolCallCount === "number" ? data.toolCallCount : runtimeState.toolCallCount;
8112
+ const nextStage = this.getRuntimeStageFromLLMPhase(phase);
8113
+ session.runtimeState = {
8114
+ ...this.updateRuntimeStage(runtimeState, nextStage, notification.timestamp || Date.now()),
8115
+ callActive: session.callActive === true,
8116
+ charCount,
8117
+ thinkingChars: typeof data.thinkingChars === "number" ? data.thinkingChars : phase === "thinking" ? charCount : runtimeState.thinkingChars,
8118
+ contentChars: typeof data.contentChars === "number" ? data.contentChars : phase === "content" ? charCount : runtimeState.contentChars,
8119
+ toolCallCount
8120
+ };
8121
+ } else if (notification.type === "llm.complete") {
8122
+ const nextStage = session.callActive === true ? runtimeState.activeToolCount > 0 ? "tool_executing" : "awaiting_runtime" : "completed";
8123
+ session.runtimeState = {
8124
+ ...this.updateRuntimeStage(runtimeState, nextStage, notification.timestamp || Date.now()),
8125
+ callActive: session.callActive === true
8126
+ };
8127
+ } else if (notification.type === "tool.start") {
8128
+ const data = notification.data && typeof notification.data === "object" ? notification.data : {};
8129
+ const toolName = typeof data.toolName === "string" ? data.toolName.trim() : "";
8130
+ const activeToolNames = this.cloneRuntimeState(runtimeState).activeToolNames;
8131
+ if (toolName && !activeToolNames.includes(toolName)) {
8132
+ activeToolNames.push(toolName);
8133
+ }
8134
+ session.runtimeState = {
8135
+ ...this.updateRuntimeStage(runtimeState, "tool_executing", notification.timestamp || Date.now()),
8136
+ callActive: session.callActive === true,
8137
+ activeToolNames,
8138
+ activeToolCount: activeToolNames.length
8139
+ };
8140
+ } else if (notification.type === "tool.complete") {
8141
+ const data = notification.data && typeof notification.data === "object" ? notification.data : {};
8142
+ const toolName = typeof data.toolName === "string" ? data.toolName.trim() : "";
8143
+ const activeToolNames = this.cloneRuntimeState(runtimeState).activeToolNames.filter((name) => name !== toolName);
8144
+ const nextStage = session.callActive === true ? activeToolNames.length > 0 ? "tool_executing" : "awaiting_runtime" : "completed";
8145
+ session.runtimeState = {
8146
+ ...this.updateRuntimeStage(runtimeState, nextStage, notification.timestamp || Date.now()),
8147
+ callActive: session.callActive === true,
8148
+ activeToolNames,
8149
+ activeToolCount: activeToolNames.length
8150
+ };
8151
+ }
7154
8152
  if (notification.category === "state") {
7155
8153
  session.currentState = notification;
7156
8154
  } else if (notification.category === "event") {
@@ -7336,6 +8334,8 @@ var ViewerWorker = class {
7336
8334
  placeholder: msg.placeholder,
7337
8335
  initialValue: msg.initialValue,
7338
8336
  actions: msg.actions,
8337
+ mode: msg.mode,
8338
+ questions: msg.questions,
7339
8339
  timestamp: Date.now()
7340
8340
  });
7341
8341
  console.log(`[Viewer Worker] Input request \u5DF2\u5B58\u50A8: ${requestId}, \u5F53\u524D\u961F\u5217\u5927\u5C0F: ${pendingRequests.size}`);
@@ -7668,4 +8668,4 @@ if (isMainModule(import.meta.url)) {
7668
8668
  export {
7669
8669
  ViewerWorker
7670
8670
  };
7671
- //# sourceMappingURL=chunk-NORTAQIL.js.map
8671
+ //# sourceMappingURL=chunk-4WK7UENZ.js.map