@codeagentswarm/cas-cloud 0.0.12 → 0.0.16

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.
package/dist/cas.js CHANGED
@@ -14842,33 +14842,30 @@ CodeAgentSwarm shows two different things per agent:
14842
14842
  the context stays clear at a glance \u2014 name the feature, NOT a low-level step or file,
14843
14843
  and NOT a work PHASE (testing, e2e, validating, reviewing, deploying). Running the e2e
14844
14844
  suite for a settings redesign is titled "Settings Redesign", never "Settings E2E Tests".
14845
- Examples: "Promo Video for Twitter", "Minimize Agents". You set it ONCE at the start
14846
- and it almost never changes \u2014 the ONLY reasons to set it again are refining it when the
14847
- goal evolves, or REPLACING it when the agent pivots to a completely different topic.
14845
+ Derive it from the **first user request** that established this conversation's
14846
+ functionality and set it ONCE per conversation. It stays stable across follow-ups,
14847
+ new turns, tasks, reviews, tests, validation, finishing, and context compaction.
14848
14848
  2. **Current activity** = the product-focused step you are doing RIGHT NOW. Example:
14849
14849
  "Implementing the analytics tracking backend". It is shown only when the user hovers
14850
14850
  the agent, and it accumulates as the activity log behind the clock button.
14851
14851
 
14852
14852
  ## \u{1F4E2} THE RULE
14853
14853
 
14854
- **\u{1F6A6} FIRST ACTION when you start working in an agent:** set the GENERAL title right away,
14855
- before anything else, so the agent is NEVER without info. It is ALWAYS safe to call \u2014 if
14856
- the user manually renamed the agent the app keeps their name (a manual rename always wins
14857
- over an MCP title). Never leave an agent with no meaningful title.
14854
+ **\u{1F6A6} FIRST ACTION in a new conversation:** set the GENERAL title from the first user
14855
+ request. On later turns, do NOT call \`set_terminal_title\`; update activity instead.
14858
14856
 
14859
14857
  **\u{1F4CD} ALWAYS leave a CURRENT SUMMARY before acting.** Right after the title, call
14860
14858
  \`update_terminal_activity\` with your first product-focused step, BEFORE running any other
14861
14859
  tool. If an agent has no activity yet, set one before you do anything else \u2014 so at every
14862
14860
  moment the user can see what each agent is doing. Never start working with no activity set.
14863
14861
 
14864
- **At the START of work \u2192 set the GENERAL title ONCE. As you work \u2192 update the ACTIVITY often.**
14862
+ **First request \u2192 set the title ONCE. Every turn \u2192 update the ACTIVITY.**
14865
14863
 
14866
- - \u2705 Set the general title once so the user always knows, at a glance, what this agent
14867
- is for. Set it again only to REFINE it when the overall goal changes (e.g. the user
14868
- expands the feature), or to REPLACE it when the topic changes RADICALLY.
14864
+ - \u2705 Keep the title from the first user request. Refining, extending, fixing, reviewing,
14865
+ validating, or finishing that request changes the activity, not the title.
14869
14866
  - \u2705 RADICAL topic change \u2192 NEW general title, MANDATORY. When the user switches this
14870
14867
  agent to a completely different topic (e.g. from "Minimize Agents" to preparing
14871
- a release), call \`set_terminal_title\` again with the new topic. Updating only the
14868
+ a release), call \`set_terminal_title\` again with \`replace_existing=true\`. Updating only the
14872
14869
  activity and leaving the old title is WRONG: the tab would keep describing work that
14873
14870
  is no longer happening.
14874
14871
  - \u2705 Update the activity every time your focus moves to a new step. This is what the user
@@ -14882,7 +14879,7 @@ the app's own static UI stays in English; these agent-written labels follow the
14882
14879
 
14883
14880
  **\u{1F527} THE TWO TOOLS:**
14884
14881
  \`\`\`
14885
- # 1) ONCE at the start - the sticky, product-level tab title
14882
+ # 1) ONCE on the conversation's first request - the sticky tab title
14886
14883
  mcp__codeagentswarm-tasks__set_terminal_title(
14887
14884
  title="Promo Video",
14888
14885
  long_title="Promotional video for the Twitter launch"
@@ -14903,9 +14900,8 @@ mcp__codeagentswarm-tasks__update_terminal_activity(
14903
14900
  in the tab. \`long_title\` is the **GOAL**: one sentence on what this agent is FOR \u2014 the
14904
14901
  outcome the work aims at, not the step you are on. The user reads it in the hover under
14905
14902
  the title, labelled GOAL, and it is the ONLY place that answers "why does this agent
14906
- exist"; six words cannot carry that. Sticky: set once, refine rarely \u2014 but ALWAYS set a
14907
- fresh one when the agent pivots to a completely different topic. Never describe a
14908
- single step here.
14903
+ exist"; six words cannot carry that. Repeated calls preserve the first title. Pass
14904
+ \`replace_existing=true\` only after the user pivots to completely different functionality.
14909
14905
  \`\`\`
14910
14906
  \u2705 title="Orden notificaci\xF3n"
14911
14907
  long_title="Que las notificaciones no salgan antes de que cambie el status del agente"
@@ -14931,7 +14927,7 @@ mcp__codeagentswarm-tasks__update_terminal_activity(
14931
14927
  1. find_related_active_tasks(...) \u2190 Check first
14932
14928
  2. create_task(...) \u2190 If needed
14933
14929
  3. start_task(task_id=123)
14934
- 4. set_terminal_title( \u2190 ONCE, AFTER start_task (captures task_id)
14930
+ 4. set_terminal_title( \u2190 only if this conversation has no title
14935
14931
  title="Minimize Agents",
14936
14932
  long_title="Add the ability to minimize and restore agents"
14937
14933
  )
@@ -14940,7 +14936,7 @@ mcp__codeagentswarm-tasks__update_terminal_activity(
14940
14936
  7. ...keep calling update_terminal_activity as your focus moves to new steps...
14941
14937
  \`\`\`
14942
14938
 
14943
- **\u{1F6A8} CRITICAL:** call set_terminal_title AFTER start_task so the task_id is captured.
14939
+ **\u{1F6A8} CRITICAL:** starting another task in the same conversation does not justify retitling it.
14944
14940
 
14945
14941
  ### \u2705 WITHOUT A TASK (research/investigation):
14946
14942
  \`\`\`
@@ -14959,13 +14955,10 @@ update_terminal_activity(activity="Implementing restore-from-minimized")
14959
14955
  update_terminal_activity(activity="Persisting minimized state across restarts")
14960
14956
  \`\`\`
14961
14957
 
14962
- ### \u2705 THE GOAL ITSELF CHANGES \u2192 refine the GENERAL title (rare):
14958
+ ### \u2705 THE ORIGINAL GOAL EXPANDS \u2192 keep the title and update ACTIVITY:
14963
14959
  \`\`\`
14964
- # The user expanded the feature, so the product goal genuinely changed:
14965
- set_terminal_title(
14966
- title="Minimize & Restore",
14967
- long_title="Minimize agents and restore them later from a tray"
14968
- )
14960
+ # The user expanded the same feature; the first title remains "Minimize Agents":
14961
+ update_terminal_activity(activity="Adding restore from the tray")
14969
14962
  \`\`\`
14970
14963
 
14971
14964
  ### \u2705 RADICAL TOPIC CHANGE \u2192 set a NEW general title (mandatory):
@@ -14974,7 +14967,8 @@ set_terminal_title(
14974
14967
  User: "leave that, now let's prepare the release notes"
14975
14968
  set_terminal_title(
14976
14969
  title="Release Notes",
14977
- long_title="Preparing the release notes for the next version"
14970
+ long_title="Preparing the release notes for the next version",
14971
+ replace_existing=true
14978
14972
  )
14979
14973
  update_terminal_activity(activity="Drafting the release notes")
14980
14974
  # \u274C WRONG: calling only update_terminal_activity here \u2014 the tab would still
@@ -14983,9 +14977,8 @@ update_terminal_activity(activity="Drafting the release notes")
14983
14977
 
14984
14978
  ## \u26A0\uFE0F WHEN TO USE WHICH
14985
14979
 
14986
- - **General title (set_terminal_title):** once at the start; again when the overall
14987
- goal/feature changes or the topic changes radically. Aim for very few calls per
14988
- session \u2014 but NEVER leave a stale title on an agent that moved to another topic.
14980
+ - **General title (set_terminal_title):** once per conversation from the first request.
14981
+ Replace only after a radical functionality pivot, with \`replace_existing=true\`.
14989
14982
  - **Current activity (update_terminal_activity):** every time you move to a new product
14990
14983
  step. Call it freely - it never disturbs the tab.
14991
14984
 
@@ -15063,14 +15056,14 @@ update_terminal_activity(activity="Investigating the output handler cost") \u
15063
15056
  **Bad 3 - Never setting a general title:**
15064
15057
  \`\`\`
15065
15058
  # Only calling update_terminal_activity, so the tab never gets a product label.
15066
- \u2705 Always set_terminal_title ONCE at the start so the tab is meaningful.
15059
+ \u2705 Set the title ONCE from the first user request so the tab is meaningful.
15067
15060
  \`\`\`
15068
15061
 
15069
15062
  **Bad 4 - Radical topic change but the title never changes:**
15070
15063
  \`\`\`
15071
15064
  # Title: "Promo Video". The user then says: "now let's debug the login crash"
15072
15065
  update_terminal_activity(activity="Debugging the login crash") \u2190 \u274C tab still says "Promo Video"
15073
- \u2705 set_terminal_title(title="Login Crash", long_title="Debugging the crash when logging in")
15066
+ \u2705 set_terminal_title(title="Login Crash", long_title="Debugging the crash when logging in", replace_existing=true)
15074
15067
  \u2705 update_terminal_activity(activity="Reproducing the login crash")
15075
15068
  \`\`\`
15076
15069
 
@@ -15111,22 +15104,21 @@ Rules:
15111
15104
 
15112
15105
  ` : ""}## \u{1F6A8} FINAL REMINDER
15113
15106
 
15114
- - Set the **general title AND the GOAL once** at the start (\`set_terminal_title(title,
15115
- long_title)\` \u2014 always both); set them again when the goal changes or the topic pivots
15116
- radically \u2014 never leave a stale title.
15107
+ - Set the **general title AND GOAL once per conversation**, from the first user request.
15108
+ Follow-ups never retitle; a radical functionality pivot uses \`replace_existing=true\`.
15117
15109
  - Keep the **current activity** flowing with \`update_terminal_activity\` (product-level),
15118
15110
  one short sentence per step.
15119
15111
  ${includeStatus ? `- Keep the **status badge** honest with \`set_terminal_status\` at every phase change.
15120
15112
  ` : ""}- The tab should stay stable; the hover + clock-button log carry the detail.
15121
15113
 
15122
15114
  **MEMORIZE:**
15123
- ${includeStatus ? `1. **Start work \u2192 \`set_terminal_title(title, long_title)\` (ONCE, the tab label AND the GOAL sentence) + \`set_terminal_status(status="working")\`**
15115
+ ${includeStatus ? `1. **First request \u2192 \`set_terminal_title(title, long_title)\` once + \`set_terminal_status(status="working")\`**
15124
15116
  2. **New step \u2192 \`update_terminal_activity\` (product step)**
15125
15117
  3. **Phase changed (waiting / testing / done) \u2192 \`set_terminal_status\`**
15126
- 4. **Goal changed or RADICAL topic change \u2192 \`set_terminal_title\` again (refined or NEW title)**
15127
- 5. **\u{1F3C1} Finishing \u2192 final \`update_terminal_activity\` summary + \`set_terminal_status\` \u2192 \`complete_task\`**` : `1. **Start work \u2192 \`set_terminal_title(title, long_title)\` (ONCE, the tab label AND the GOAL sentence)**
15118
+ 4. **Radical functionality pivot \u2192 \`set_terminal_title(..., replace_existing=true)\`**
15119
+ 5. **\u{1F3C1} Finishing \u2192 final \`update_terminal_activity\` summary + \`set_terminal_status\` \u2192 \`complete_task\`**` : `1. **First request \u2192 \`set_terminal_title(title, long_title)\` once**
15128
15120
  2. **New step \u2192 \`update_terminal_activity\` (product step)**
15129
- 3. **Goal changed or RADICAL topic change \u2192 \`set_terminal_title\` again (refined or NEW title)**
15121
+ 3. **Radical functionality pivot \u2192 \`set_terminal_title(..., replace_existing=true)\`**
15130
15122
  4. **\u{1F3C1} Finishing \u2192 final \`update_terminal_activity\` summary \u2192 \`complete_task\`**`}
15131
15123
 
15132
15124
  ### \u{1F4DD} Task Plan Management
@@ -15374,8 +15366,9 @@ Avoid token limits:
15374
15366
  - Creating new? \u2192 YES
15375
15367
 
15376
15368
  **Agent Title?**
15377
- - ALWAYS set it immediately when starting work
15378
- - Radical topic change \u2192 set a NEW title (don't leave a stale one)
15369
+ - Set it from the first user request, once per conversation
15370
+ - Follow-up work changes activity, never the title
15371
+ - Radical functionality pivot \u2192 replace with \`replace_existing=true\`
15379
15372
 
15380
15373
  **Task Continuation?**
15381
15374
  - Same work <24h \u2192 Continue
@@ -15416,33 +15409,30 @@ CodeAgentSwarm shows two different things per agent:
15416
15409
  the context stays clear at a glance \u2014 name the feature, NOT a low-level step or file,
15417
15410
  and NOT a work PHASE (testing, e2e, validating, reviewing, deploying). Running the e2e
15418
15411
  suite for a settings redesign is titled "Settings Redesign", never "Settings E2E Tests".
15419
- Examples: "Promo Video for Twitter", "Minimize Agents". You set it ONCE at the start
15420
- and it almost never changes \u2014 the ONLY reasons to set it again are refining it when the
15421
- goal evolves, or REPLACING it when the agent pivots to a completely different topic.
15412
+ Derive it from the **first user request** that established this conversation's
15413
+ functionality and set it ONCE per conversation. It stays stable across follow-ups,
15414
+ new turns, tasks, reviews, tests, validation, finishing, and context compaction.
15422
15415
  2. **Current activity** = the product-focused step you are doing RIGHT NOW. Example:
15423
15416
  "Implementing the analytics tracking backend". It is shown only when the user hovers
15424
15417
  the agent, and it accumulates as the activity log behind the clock button.
15425
15418
 
15426
15419
  ## \u{1F4E2} THE RULE
15427
15420
 
15428
- **\u{1F6A6} FIRST ACTION when you start working in an agent:** set the GENERAL title right away,
15429
- before anything else, so the agent is NEVER without info. It is ALWAYS safe to call \u2014 if
15430
- the user manually renamed the agent the app keeps their name (a manual rename always wins
15431
- over an MCP title). Never leave an agent with no meaningful title.
15421
+ **\u{1F6A6} FIRST ACTION in a new conversation:** set the GENERAL title from the first user
15422
+ request. On later turns, do NOT call \`set_terminal_title\`; update activity instead.
15432
15423
 
15433
15424
  **\u{1F4CD} ALWAYS leave a CURRENT SUMMARY before acting.** Right after the title, call
15434
15425
  \`update_terminal_activity\` with your first product-focused step, BEFORE running any other
15435
15426
  tool. If an agent has no activity yet, set one before you do anything else \u2014 so at every
15436
15427
  moment the user can see what each agent is doing. Never start working with no activity set.
15437
15428
 
15438
- **At the START of work \u2192 set the GENERAL title ONCE. As you work \u2192 update the ACTIVITY often.**
15429
+ **First request \u2192 set the title ONCE. Every turn \u2192 update the ACTIVITY.**
15439
15430
 
15440
- - \u2705 Set the general title once so the user always knows, at a glance, what this agent
15441
- is for. Set it again only to REFINE it when the overall goal changes (e.g. the user
15442
- expands the feature), or to REPLACE it when the topic changes RADICALLY.
15431
+ - \u2705 Keep the title from the first user request. Refining, extending, fixing, reviewing,
15432
+ validating, or finishing that request changes the activity, not the title.
15443
15433
  - \u2705 RADICAL topic change \u2192 NEW general title, MANDATORY. When the user switches this
15444
15434
  agent to a completely different topic (e.g. from "Minimize Agents" to preparing
15445
- a release), call \`set_terminal_title\` again with the new topic. Updating only the
15435
+ a release), call \`set_terminal_title\` again with \`replace_existing=true\`. Updating only the
15446
15436
  activity and leaving the old title is WRONG: the tab would keep describing work that
15447
15437
  is no longer happening.
15448
15438
  - \u2705 Update the activity every time your focus moves to a new step. This is what the user
@@ -15456,7 +15446,7 @@ the app's own static UI stays in English; these agent-written labels follow the
15456
15446
 
15457
15447
  **\u{1F527} THE TWO TOOLS:**
15458
15448
  \`\`\`
15459
- # 1) ONCE at the start - the sticky, product-level tab title
15449
+ # 1) ONCE on the conversation's first request - the sticky tab title
15460
15450
  mcp__codeagentswarm-tasks__set_terminal_title(
15461
15451
  title="Promo Video",
15462
15452
  long_title="Promotional video for the Twitter launch"
@@ -15477,9 +15467,8 @@ mcp__codeagentswarm-tasks__update_terminal_activity(
15477
15467
  in the tab. \`long_title\` is the **GOAL**: one sentence on what this agent is FOR \u2014 the
15478
15468
  outcome the work aims at, not the step you are on. The user reads it in the hover under
15479
15469
  the title, labelled GOAL, and it is the ONLY place that answers "why does this agent
15480
- exist"; six words cannot carry that. Sticky: set once, refine rarely \u2014 but ALWAYS set a
15481
- fresh one when the agent pivots to a completely different topic. Never describe a
15482
- single step here.
15470
+ exist"; six words cannot carry that. Repeated calls preserve the first title. Pass
15471
+ \`replace_existing=true\` only after the user pivots to completely different functionality.
15483
15472
  \`\`\`
15484
15473
  \u2705 title="Orden notificaci\xF3n"
15485
15474
  long_title="Que las notificaciones no salgan antes de que cambie el status del agente"
@@ -15498,9 +15487,9 @@ mcp__codeagentswarm-tasks__update_terminal_activity(
15498
15487
 
15499
15488
  ## \u{1F3AF} WORKFLOW
15500
15489
 
15501
- ### \u2705 WHEN STARTING WORK:
15490
+ ### \u2705 ON THE CONVERSATION'S FIRST REQUEST:
15502
15491
  \`\`\`
15503
- 1. set_terminal_title( \u2190 ONCE, product goal
15492
+ 1. set_terminal_title( \u2190 once per conversation
15504
15493
  title="Minimize Agents",
15505
15494
  long_title="Add the ability to minimize and restore agents"
15506
15495
  )
@@ -15515,13 +15504,10 @@ update_terminal_activity(activity="Implementing restore-from-minimized")
15515
15504
  update_terminal_activity(activity="Persisting minimized state across restarts")
15516
15505
  \`\`\`
15517
15506
 
15518
- ### \u2705 THE GOAL ITSELF CHANGES \u2192 refine the GENERAL title (rare):
15507
+ ### \u2705 THE ORIGINAL GOAL EXPANDS \u2192 keep the title and update ACTIVITY:
15519
15508
  \`\`\`
15520
- # The user expanded the feature, so the product goal genuinely changed:
15521
- set_terminal_title(
15522
- title="Minimize & Restore",
15523
- long_title="Minimize agents and restore them later from a tray"
15524
- )
15509
+ # The user expanded the same feature; the first title remains "Minimize Agents":
15510
+ update_terminal_activity(activity="Adding restore from the tray")
15525
15511
  \`\`\`
15526
15512
 
15527
15513
  ### \u2705 RADICAL TOPIC CHANGE \u2192 set a NEW general title (mandatory):
@@ -15530,7 +15516,8 @@ set_terminal_title(
15530
15516
  User: "leave that, now let's prepare the release notes"
15531
15517
  set_terminal_title(
15532
15518
  title="Release Notes",
15533
- long_title="Preparing the release notes for the next version"
15519
+ long_title="Preparing the release notes for the next version",
15520
+ replace_existing=true
15534
15521
  )
15535
15522
  update_terminal_activity(activity="Drafting the release notes")
15536
15523
  # \u274C WRONG: calling only update_terminal_activity here \u2014 the tab would still
@@ -15539,9 +15526,8 @@ update_terminal_activity(activity="Drafting the release notes")
15539
15526
 
15540
15527
  ## \u26A0\uFE0F WHEN TO USE WHICH
15541
15528
 
15542
- - **General title (set_terminal_title):** once at the start; again when the overall
15543
- goal/feature changes or the topic changes radically. Aim for very few calls per
15544
- session \u2014 but NEVER leave a stale title on an agent that moved to another topic.
15529
+ - **General title (set_terminal_title):** once per conversation from the first request.
15530
+ Replace only after a radical functionality pivot, with \`replace_existing=true\`.
15545
15531
  - **Current activity (update_terminal_activity):** every time you move to a new product
15546
15532
  step. Call it freely - it never disturbs the tab.
15547
15533
 
@@ -15617,14 +15603,14 @@ update_terminal_activity(activity="Investigating the output handler cost") \u
15617
15603
  **Bad 3 - Never setting a general title:**
15618
15604
  \`\`\`
15619
15605
  # Only calling update_terminal_activity, so the tab never gets a product label.
15620
- \u2705 Always set_terminal_title ONCE at the start so the tab is meaningful.
15606
+ \u2705 Set the title ONCE from the first user request so the tab is meaningful.
15621
15607
  \`\`\`
15622
15608
 
15623
15609
  **Bad 4 - Radical topic change but the title never changes:**
15624
15610
  \`\`\`
15625
15611
  # Title: "Promo Video". The user then says: "now let's debug the login crash"
15626
15612
  update_terminal_activity(activity="Debugging the login crash") \u2190 \u274C tab still says "Promo Video"
15627
- \u2705 set_terminal_title(title="Login Crash", long_title="Debugging the crash when logging in")
15613
+ \u2705 set_terminal_title(title="Login Crash", long_title="Debugging the crash when logging in", replace_existing=true)
15628
15614
  \u2705 update_terminal_activity(activity="Reproducing the login crash")
15629
15615
  \`\`\`
15630
15616
 
@@ -15639,26 +15625,25 @@ set_terminal_title(title="Settings E2E Tests") \u2190 \u274C testing is a STEP
15639
15625
 
15640
15626
  ## \u{1F6A8} FINAL REMINDER
15641
15627
 
15642
- - Set the **general title AND the GOAL once** at the start (\`set_terminal_title(title,
15643
- long_title)\` \u2014 always both); set them again when the goal changes or the topic pivots
15644
- radically \u2014 never leave a stale title.
15628
+ - Set the **general title AND GOAL once per conversation**, from the first user request.
15629
+ Follow-ups never retitle; a radical functionality pivot uses \`replace_existing=true\`.
15645
15630
  - Keep the **current activity** flowing with \`update_terminal_activity\` (product-level),
15646
15631
  one short sentence per step.
15647
15632
  - The tab should stay stable; the hover + clock-button log carry the detail.
15648
15633
  - Do NOT create or manage tasks \u2014 automatic task management is disabled.
15649
15634
 
15650
15635
  **MEMORIZE:**
15651
- 1. **Start work \u2192 \`set_terminal_title(title, long_title)\` (ONCE, the tab label AND the GOAL sentence)**
15636
+ 1. **First request \u2192 \`set_terminal_title(title, long_title)\` once**
15652
15637
  2. **New step \u2192 \`update_terminal_activity\` (product step)**
15653
- 3. **Goal changed or RADICAL topic change \u2192 \`set_terminal_title\` again (refined or NEW title)**
15638
+ 3. **Radical functionality pivot \u2192 \`set_terminal_title(..., replace_existing=true)\`**
15654
15639
  4. **\u{1F3C1} Finishing \u2192 final \`update_terminal_activity\` summary**
15655
15640
 
15656
15641
  ## Quick Decision Guide
15657
15642
 
15658
15643
  **Agent Title?**
15659
- - ALWAYS set it immediately when starting work
15660
- - Refine when the overall goal changes
15661
- - Radical topic change \u2192 set a NEW title (don't leave a stale one)
15644
+ - Set it from the first user request, once per conversation
15645
+ - Follow-up work changes activity, never the title
15646
+ - Radical functionality pivot \u2192 replace with \`replace_existing=true\`
15662
15647
 
15663
15648
  **Tasks?**
15664
15649
  - Automatic task management is DISABLED \u2014 never create or manage tasks
@@ -18879,6 +18864,7 @@ var require_provider_auth = __commonJS({
18879
18864
  /\bunauthenticated\b/i,
18880
18865
  /\bauthentication required\b/i,
18881
18866
  /\bplease (?:run|use) [^\n]*(?:login|log in|sign in)\b/i,
18867
+ /\brun [^\n]*(?:login|log in|sign in)\b/i,
18882
18868
  /\b(?:login|log in|sign in) (?:is )?required\b/i,
18883
18869
  /\bmissing (?:an? )?(?:api key|access token|auth token|credentials?)\b/i,
18884
18870
  /\b(?:invalid|expired) (?:api key|access token|auth token|credentials?)\b/i,
@@ -19240,7 +19226,6 @@ var require_codex_app_server_driver = __commonJS({
19240
19226
  var DEFAULT_SERVICE_TIER_ID = "default";
19241
19227
  var EPHEMERAL_CODEX_HOME_PREFIX = "codeagentswarm-codex-home-";
19242
19228
  var EPHEMERAL_CODEX_HOME_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
19243
- var REQUIRED_TASK_MCP_TOOL = "check_active";
19244
19229
  var TEXT_ONLY_INSTRUCTIONS = "Answer with plain text only. Do not call tools, commands, apps, skills, web search, MCP servers, or subagents.";
19245
19230
  var IMAGE_ONLY_INSTRUCTIONS = "Generate exactly one image with the built-in image generation capability. Do not call commands, apps, skills, web search, MCP servers, subagents, or any other tool. Do not return prose.";
19246
19231
  var TEXT_ONLY_CONFIG_OVERRIDES = Object.freeze([
@@ -19397,10 +19382,6 @@ var require_codex_app_server_driver = __commonJS({
19397
19382
  * @param {{name: string, title: string, version: string}} [options.clientInfo]
19398
19383
  * @param {number} [options.requestTimeoutMs=30000] Per-request timeout.
19399
19384
  * @param {Function} [options.spawnFn] Injectable spawn, for tests.
19400
- * @param {string} [options.requiredMcpServer] MCP server whose activation
19401
- * check is repaired before the first turn when possible.
19402
- * @param {Function} [options.repairMcpConfig] Repairs the on-disk MCP config
19403
- * before the running app-server reloads it.
19404
19385
  * @param {{register: Function, unregister: Function}} [options.processRegistry]
19405
19386
  * PID registry the child is tracked in; injectable for tests.
19406
19387
  */
@@ -19410,9 +19391,7 @@ var require_codex_app_server_driver = __commonJS({
19410
19391
  clientInfo,
19411
19392
  requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS,
19412
19393
  spawnFn,
19413
- processRegistry,
19414
- requiredMcpServer,
19415
- repairMcpConfig
19394
+ processRegistry
19416
19395
  } = {}) {
19417
19396
  super();
19418
19397
  this._binaryPath = binaryPath;
@@ -19421,8 +19400,6 @@ var require_codex_app_server_driver = __commonJS({
19421
19400
  this._requestTimeoutMs = requestTimeoutMs;
19422
19401
  this._spawnFn = spawnFn || spawn;
19423
19402
  this._processRegistry = processRegistry || require_spawned_process_registry();
19424
- this._requiredMcpServer = requiredMcpServer || null;
19425
- this._repairMcpConfig = repairMcpConfig || null;
19426
19403
  this._child = null;
19427
19404
  this._pendingRequests = /* @__PURE__ */ new Map();
19428
19405
  this._pendingApprovals = /* @__PURE__ */ new Map();
@@ -19969,7 +19946,7 @@ var require_codex_app_server_driver = __commonJS({
19969
19946
  initialTurnsPage: {
19970
19947
  limit: INITIAL_HISTORY_TURN_PAGE_SIZE,
19971
19948
  sortDirection: "desc",
19972
- itemsView: "summary"
19949
+ itemsView: "full"
19973
19950
  },
19974
19951
  ...threadParams
19975
19952
  });
@@ -19995,13 +19972,6 @@ var require_codex_app_server_driver = __commonJS({
19995
19972
  }
19996
19973
  }
19997
19974
  this._threadId = ((_a = result == null ? void 0 : result.thread) == null ? void 0 : _a.id) || (result == null ? void 0 : result.threadId) || (resumeSessionId || null);
19998
- if (!toolsDisabled && !imageGenerationOnly) {
19999
- try {
20000
- await this._ensureRequiredMcpServer(this._threadId);
20001
- } catch (error) {
20002
- console.warn(`[CodexAppServerDriver] Continuing without verified task MCP tools: ${error.message}`);
20003
- }
20004
- }
20005
19975
  this._sessionCwd = (result == null ? void 0 : result.cwd) || ((_b = result == null ? void 0 : result.thread) == null ? void 0 : _b.cwd) || cwd;
20006
19976
  let historyPage = result == null ? void 0 : result.initialTurnsPage;
20007
19977
  const returnedTurns = Array.isArray((_c = result == null ? void 0 : result.thread) == null ? void 0 : _c.turns) ? result.thread.turns : [];
@@ -20011,7 +19981,7 @@ var require_codex_app_server_driver = __commonJS({
20011
19981
  threadId: this._threadId,
20012
19982
  limit: INITIAL_HISTORY_TURN_PAGE_SIZE,
20013
19983
  sortDirection: "desc",
20014
- itemsView: "summary"
19984
+ itemsView: "full"
20015
19985
  });
20016
19986
  } catch (_) {
20017
19987
  }
@@ -20060,15 +20030,6 @@ var require_codex_app_server_driver = __commonJS({
20060
20030
  } while (cursor);
20061
20031
  return servers;
20062
20032
  }
20063
- async _ensureRequiredMcpServer(threadId) {
20064
- if (!this._requiredMcpServer) return;
20065
- const connected = (servers) => servers.some((server) => (server == null ? void 0 : server.name) === this._requiredMcpServer && Object.values(server.tools || {}).some((tool) => (tool == null ? void 0 : tool.name) === REQUIRED_TASK_MCP_TOOL));
20066
- if (connected(await this._listMcpServers(threadId))) return;
20067
- if (this._repairMcpConfig && await this._repairMcpConfig() === false) {
20068
- throw new Error("CodeAgentSwarm could not repair the Codex MCP configuration. Restart CodeAgentSwarm and retry Chat.");
20069
- }
20070
- await this._request("config/mcpServer/reload", null);
20071
- }
20072
20033
  /**
20073
20034
  * Build a resumed thread's past items as historical `item.completed` events.
20074
20035
  *
@@ -30461,6 +30422,18 @@ var require_mcp_stdio_server = __commonJS({
30461
30422
  } catch (e) {
30462
30423
  }
30463
30424
  }
30425
+ _hasNudgeMarker(kind) {
30426
+ try {
30427
+ const os2 = require("os");
30428
+ const fs2 = require("fs");
30429
+ const stateDir = path.join(os2.homedir(), ".codeagentswarm", "terminal-nudge-state");
30430
+ const rawKey = process.env.CODEAGENTSWARM_TERMINAL_ID || process.env.CODEAGENTSWARM_CURRENT_QUADRANT;
30431
+ const key = String(rawKey || "").replace(/[^A-Za-z0-9_-]/g, "_");
30432
+ return !!key && fs2.existsSync(path.join(stateDir, `${key}.${kind}`));
30433
+ } catch (e) {
30434
+ return false;
30435
+ }
30436
+ }
30464
30437
  // Resolve the current terminal id (1-based) from the environment.
30465
30438
  _getCurrentTerminalId() {
30466
30439
  const terminalId = process.env.CODEAGENTSWARM_CURRENT_QUADRANT;
@@ -30479,20 +30452,30 @@ var require_mcp_stdio_server = __commonJS({
30479
30452
  }
30480
30453
  // Set the GENERAL terminal title: the sticky tab label that represents the
30481
30454
  // product-level goal of what this terminal is working on. Set it ONCE at the
30482
- // start of work; call it again only to refine when the overall functionality
30483
- // changes, or to replace it when the terminal pivots to a radically different
30484
- // topic. It does NOT represent the current step — use update_terminal_activity
30485
- // for that.
30455
+ // start of the conversation. Later calls preserve it unless the caller explicitly
30456
+ // declares a radical topic change. It does NOT represent the current step — use
30457
+ // update_terminal_activity for that.
30486
30458
  async setTerminalTitle(params) {
30487
- const { title, long_title } = params;
30459
+ const { title, long_title, replace_existing } = params;
30488
30460
  if (!title) {
30489
30461
  throw new Error("title is required");
30490
30462
  }
30491
30463
  if (isPlaceholderTitle(title)) {
30492
30464
  throw new Error(`"${title}" looks like example text copied from the tool documentation, not a real title. Call the tool again with a title that describes the ACTUAL feature or goal you are working on in this agent (max 6 words).`);
30493
30465
  }
30494
- const shortTitle = this.generateShortTitle(title);
30466
+ if (replace_existing !== void 0 && typeof replace_existing !== "boolean") {
30467
+ throw new Error("replace_existing must be a boolean");
30468
+ }
30495
30469
  const terminalId = this._getCurrentTerminalId();
30470
+ if (replace_existing !== true && this._hasNudgeMarker("title")) {
30471
+ return {
30472
+ terminal_id: terminalId,
30473
+ updated: false,
30474
+ preserved: true,
30475
+ message: "The existing conversation title was preserved. Use update_terminal_activity for follow-up work; set replace_existing=true only after the user pivots to a completely different functionality."
30476
+ };
30477
+ }
30478
+ const shortTitle = this.generateShortTitle(title);
30496
30479
  const currentTask = this._getCurrentTask(terminalId);
30497
30480
  const taskId = currentTask ? currentTask.id : null;
30498
30481
  let longTitle = long_title;
@@ -31390,7 +31373,7 @@ Use 'clear' to remove the status.`;
31390
31373
  },
31391
31374
  {
31392
31375
  name: "set_terminal_title",
31393
- description: 'Set the GENERAL agent title AND the agent GOAL \u2014 pass BOTH arguments, because the user sees them together. `title` is the sticky Agent tab label at the FEATURE level (e.g. "Promo video for Twitter", "Minimize agents"); `long_title` is one sentence on what this agent is FOR, shown in the hover under the title. Keep the title high-level: name the feature, not a low-level step or work phase. Set it once at the start, then only refine it when the overall goal changes. Use update_terminal_activity for the current step. LANGUAGE: write BOTH in the SAME language the user is speaking.',
31376
+ description: "Set the GENERAL agent title AND the agent GOAL from the first user request that established this conversation's topic. Pass BOTH text arguments because the user sees them together. This is once per conversation, NOT once per user message, turn, task, review, validation, or resumed session. Follow-up work belongs in update_terminal_activity and repeated calls preserve the original title. Only a user pivot to a completely different functionality may replace it, by passing replace_existing=true. LANGUAGE: write both text fields in the SAME language the user is speaking.",
31394
31377
  inputSchema: {
31395
31378
  type: "object",
31396
31379
  properties: {
@@ -31401,6 +31384,10 @@ Use 'clear' to remove the status.`;
31401
31384
  long_title: {
31402
31385
  type: "string",
31403
31386
  description: `The agent GOAL: one sentence saying what this agent is FOR \u2014 the outcome the work is aiming at, not the current step. ALWAYS provide it: the user reads it in the Agent hover, labelled GOAL, under the short title. Write the real goal in the user's language. Do NOT restate the title or prefix it with "Working on:".`
31387
+ },
31388
+ replace_existing: {
31389
+ type: "boolean",
31390
+ description: "Leave false or omit for normal follow-up work. Set true ONLY when the user explicitly pivots this existing conversation to a completely different functionality; reviews, tests, fixes, implementation phases, and refinements of the original request are activities and must not replace the title."
31404
31391
  }
31405
31392
  },
31406
31393
  // long_title is NOT in `required` on purpose: some agent clients
@@ -31854,7 +31841,8 @@ Use 'clear' to remove the status.`;
31854
31841
  case "set_terminal_title":
31855
31842
  result = await this.setTerminalTitle({
31856
31843
  title: args.title,
31857
- long_title: args.long_title
31844
+ long_title: args.long_title,
31845
+ replace_existing: args.replace_existing
31858
31846
  });
31859
31847
  break;
31860
31848
  case "update_terminal_activity":
@@ -33683,18 +33671,13 @@ var require_driver_chat_manager = __commonJS({
33683
33671
  });
33684
33672
  });
33685
33673
  }
33686
- function defaultCreateDriver({ agent, env, binaryPath, requiredMcpServer, repairMcpConfig }) {
33674
+ function defaultCreateDriver({ agent, env, binaryPath }) {
33687
33675
  if (agent === "claude") return new ClaudeAgentSdkDriver({ env, binaryPath });
33688
33676
  if (agent === "antigravity") return new AntigravityPrintDriver({ env, binaryPath });
33689
33677
  if (agent === "opencode" || agent === "kimi" || agent === "grok" || agent === "cursor") {
33690
33678
  return new AcpAgentDriver({ provider: agent, env, binaryPath });
33691
33679
  }
33692
- return new CodexAppServerDriver({
33693
- env,
33694
- binaryPath,
33695
- requiredMcpServer,
33696
- repairMcpConfig
33697
- });
33680
+ return new CodexAppServerDriver({ env, binaryPath });
33698
33681
  }
33699
33682
  var DriverChatManager = class extends EventEmitter {
33700
33683
  /**
@@ -33961,6 +33944,7 @@ var require_driver_chat_manager = __commonJS({
33961
33944
  // main-process placeholder, so return that same UUID for persistence
33962
33945
  // and instance-safe MCP notification routing.
33963
33946
  terminalUuid: env.CODEAGENTSWARM_TERMINAL_ID || null,
33947
+ communicationEnabled: env.CODEAGENTSWARM_SESSION_COMMUNICATION_ENABLED === "1",
33964
33948
  model: session.model,
33965
33949
  effort: session.effort || resolvedEffort,
33966
33950
  serviceTier: session.serviceTier ?? (rejected.has("serviceTier") ? null : (_a = selections.find((selection) => (selection == null ? void 0 : selection.id) === "serviceTier")) == null ? void 0 : _a.value) ?? null,
@@ -34722,11 +34706,13 @@ var require_chat_history_pagination = __commonJS({
34722
34706
  return Date.parse(value);
34723
34707
  }
34724
34708
  function normalizeConversationMessages(messages) {
34709
+ let internalTurn = false;
34725
34710
  return (Array.isArray(messages) ? messages : []).flatMap((message) => {
34726
34711
  const role = (message == null ? void 0 : message.role) === "assistant" ? "assistant_message" : (message == null ? void 0 : message.role) === "user" ? "user_message" : null;
34727
34712
  const text = [message == null ? void 0 : message.content, message == null ? void 0 : message.text, message == null ? void 0 : message.displayText].find((value) => typeof value === "string" && value.trim());
34728
34713
  const attachments = Array.isArray(message == null ? void 0 : message.attachments) ? message.attachments : [];
34729
- return role && (text || attachments.length) && !(role === "user_message" && text && parseSessionCoordinationPrompt(text)) ? [{ role, text: text || "", attachments, timestamp: message.timestamp }] : [];
34714
+ if (role === "user_message") internalTurn = Boolean(text && parseSessionCoordinationPrompt(text));
34715
+ return role && (text || attachments.length) && !internalTurn ? [{ role, text: text || "", attachments, timestamp: message.timestamp }] : [];
34730
34716
  });
34731
34717
  }
34732
34718
  function boundedConversationMessages(messages, { limit = 30, maxMessageChars = 8e3, maxBytes = 22e4 } = {}) {
@@ -34777,12 +34763,14 @@ var require_chat_history_pagination = __commonJS({
34777
34763
  before,
34778
34764
  anchor,
34779
34765
  knownCount = 0,
34766
+ hydratedCount = 0,
34780
34767
  limit = DEFAULT_HISTORY_PAGE_SIZE
34781
34768
  } = {}) {
34782
34769
  const normalized = normalizeConversationMessages(messages);
34783
34770
  const safeLimit = Number.isSafeInteger(limit) && limit > 0 ? limit : DEFAULT_HISTORY_PAGE_SIZE;
34784
34771
  const anchorIndex = findConversationAnchor(normalized, anchor);
34785
- const end = Number.isSafeInteger(before) ? Math.max(0, Math.min(before, normalized.length)) : anchorIndex ?? Math.max(0, normalized.length - Math.max(0, Number(knownCount) || 0));
34772
+ const hydratedEnd = Math.max(0, normalized.length - Math.max(0, Number(hydratedCount) || 0));
34773
+ const end = Number.isSafeInteger(before) ? Math.max(0, Math.min(before, normalized.length)) : Math.min(hydratedEnd, anchorIndex ?? Math.max(0, normalized.length - Math.max(0, Number(knownCount) || 0)));
34786
34774
  const start = Math.max(0, end - safeLimit);
34787
34775
  return {
34788
34776
  messages: normalized.slice(start, end).map((message, offset) => ({
@@ -35048,6 +35036,7 @@ var require_mobile_runtime = __commonJS({
35048
35036
  var { parseSessionCoordinationPrompt } = require_session_coordination_message();
35049
35037
  var {
35050
35038
  conversationMessagesToEvents,
35039
+ normalizeConversationMessages,
35051
35040
  pageConversationMessages
35052
35041
  } = require_chat_history_pagination();
35053
35042
  var PROTOCOL_VERSION = 2;
@@ -35353,6 +35342,7 @@ var require_mobile_runtime = __commonJS({
35353
35342
  listProjectDirectories = null,
35354
35343
  createProject = null,
35355
35344
  updateProject = null,
35345
+ gitAvailability = null,
35356
35346
  projectIconAvailability = null,
35357
35347
  generateProjectIcon = null,
35358
35348
  registerProject = null,
@@ -35423,6 +35413,7 @@ var require_mobile_runtime = __commonJS({
35423
35413
  this.listProjectDirectories = listProjectDirectories;
35424
35414
  this.createProject = createProject;
35425
35415
  this.updateProject = updateProject;
35416
+ this.gitAvailability = gitAvailability;
35426
35417
  this.projectIconAvailability = projectIconAvailability;
35427
35418
  this.generateProjectIcon = generateProjectIcon;
35428
35419
  this.registerProject = registerProject;
@@ -35568,6 +35559,7 @@ var require_mobile_runtime = __commonJS({
35568
35559
  accountLabel: session.accountLabel,
35569
35560
  threadId: session.threadId,
35570
35561
  terminalUuid: session.terminalUuid,
35562
+ communicationEnabled: session.communicationEnabled === true,
35571
35563
  terminalOrder: session.terminalOrder,
35572
35564
  cwd: session.cwd,
35573
35565
  model: session.model,
@@ -35875,13 +35867,16 @@ var require_mobile_runtime = __commonJS({
35875
35867
  });
35876
35868
  }
35877
35869
  publishProjectIconEvent(event = {}) {
35870
+ const iconDataUrl = typeof event.iconDataUrl === "string" && event.iconDataUrl.length <= 1e5 ? event.iconDataUrl : null;
35878
35871
  return this._publish("project.icon.generated", {
35879
35872
  jobId: cleanText(event.jobId, 100),
35880
35873
  projectId: cleanText(event.projectId, 128),
35881
35874
  success: event.success === true,
35882
35875
  applied: event.applied === true,
35883
35876
  unavailable: event.unavailable === true,
35884
- ...cleanText(event.error, 500) ? { error: cleanText(event.error, 500) } : {}
35877
+ ...cleanText(event.error, 500) ? { error: cleanText(event.error, 500) } : {},
35878
+ ...cleanText(event.icon, 300) ? { icon: cleanText(event.icon, 300) } : {},
35879
+ ...iconDataUrl ? { iconDataUrl } : {}
35885
35880
  });
35886
35881
  }
35887
35882
  publishProviderLoginEvent(event = {}) {
@@ -35939,6 +35934,7 @@ var require_mobile_runtime = __commonJS({
35939
35934
  accountLabel: started.accountLabel || null,
35940
35935
  threadId: started.threadId,
35941
35936
  terminalUuid: started.terminalUuid,
35937
+ communicationEnabled: started.communicationEnabled === true,
35942
35938
  terminalOrder: Number.isSafeInteger(started.terminalId) && started.terminalId > 0 ? started.terminalId : null,
35943
35939
  desktopTerminalId: Number.isSafeInteger(started.terminalId) && started.terminalId > 0 ? started.terminalId : null,
35944
35940
  cwd: started.cwd,
@@ -35964,6 +35960,7 @@ var require_mobile_runtime = __commonJS({
35964
35960
  accountLabel: started.accountLabel || null,
35965
35961
  threadId: started.threadId,
35966
35962
  terminalUuid: started.terminalUuid,
35963
+ communicationEnabled: started.communicationEnabled === true,
35967
35964
  terminalOrder: Number.isSafeInteger(started.terminalId) && started.terminalId > 0 ? started.terminalId : null,
35968
35965
  cwd: started.cwd,
35969
35966
  model: started.model,
@@ -36019,11 +36016,11 @@ var require_mobile_runtime = __commonJS({
36019
36016
  return;
36020
36017
  }
36021
36018
  if (!Array.isArray(messages) || !messages.length) return;
36022
- const recent = messages.slice(-200);
36019
+ const recent = normalizeConversationMessages(messages).slice(-200);
36023
36020
  recent.forEach((message, index) => {
36024
36021
  var _a2, _b2;
36025
- const role = (message == null ? void 0 : message.role) === "assistant" ? "assistant_message" : (message == null ? void 0 : message.role) === "user" ? "user_message" : null;
36026
- const text = cleanText((message == null ? void 0 : message.content) ?? (message == null ? void 0 : message.text) ?? (message == null ? void 0 : message.displayText), MAX_INITIAL_PROMPT_CHARS);
36022
+ const role = message.role;
36023
+ const text = cleanText(message.text, MAX_INITIAL_PROMPT_CHARS);
36027
36024
  const attachments = Array.isArray(message == null ? void 0 : message.attachments) ? message.attachments : [];
36028
36025
  if (!role || !text && !attachments.length) return;
36029
36026
  const key = `${role}\0${text}`;
@@ -36315,6 +36312,7 @@ var require_mobile_runtime = __commonJS({
36315
36312
  accountLabel: null,
36316
36313
  threadId: null,
36317
36314
  terminalUuid: null,
36315
+ communicationEnabled: false,
36318
36316
  terminalOrder: null,
36319
36317
  desktopTerminalId: null,
36320
36318
  cwd: null,
@@ -36579,7 +36577,7 @@ var require_mobile_runtime = __commonJS({
36579
36577
  }
36580
36578
  }
36581
36579
  async _executeCommand(command = {}, context = {}) {
36582
- var _a, _b, _c, _d, _e, _f;
36580
+ var _a, _b, _c, _d, _e, _f, _g, _h;
36583
36581
  this._pruneAttachmentUploads();
36584
36582
  const sessionId = command.sessionId;
36585
36583
  const payload = command.payload || {};
@@ -36689,10 +36687,15 @@ var require_mobile_runtime = __commonJS({
36689
36687
  this.publishProjects();
36690
36688
  return result;
36691
36689
  }
36690
+ if (command.type === "project.git.availability") {
36691
+ if (typeof this.gitAvailability !== "function") return { available: false };
36692
+ exactPayload(["rootId"]);
36693
+ return this.gitAvailability({ rootId: cleanText(payload.rootId, 128) });
36694
+ }
36692
36695
  if (command.type === "project.icon.availability") {
36693
36696
  if (typeof this.projectIconAvailability !== "function") return { available: false };
36694
- exactPayload([]);
36695
- return this.projectIconAvailability();
36697
+ exactPayload(["projectId"]);
36698
+ return this.projectIconAvailability({ projectId: cleanText(payload.projectId, 128) });
36696
36699
  }
36697
36700
  if (command.type === "project.icon.generate") {
36698
36701
  if (typeof this.generateProjectIcon !== "function") throw new Error("Codex icon generation is unavailable");
@@ -36706,13 +36709,23 @@ var require_mobile_runtime = __commonJS({
36706
36709
  if (command.type === "project.register") {
36707
36710
  if (typeof this.registerProject !== "function") throw new Error("Remote project registration is unavailable");
36708
36711
  exactPayload(["rootId", "relativePath", "requestId"]);
36709
- return this.registerProject({ rootId: payload.rootId, relativePath: payload.relativePath, requestId: mutationRequestId() });
36712
+ const result = await this.registerProject({ rootId: payload.rootId, relativePath: payload.relativePath, requestId: mutationRequestId() });
36713
+ this.publishProjects();
36714
+ return result;
36710
36715
  }
36711
36716
  if (command.type === "project.clone") {
36712
36717
  if (typeof this.cloneProject !== "function") throw new Error("Remote project cloning is unavailable");
36713
- exactPayload(["rootId", "url", "relativePath", "requestId"]);
36718
+ exactPayload(["rootId", "url", "relativePath", "displayName", "color", "icon", "requestId"]);
36714
36719
  if (typeof payload.requestId !== "string" || !payload.requestId) throw new Error("A clone requestId is required");
36715
- return this.cloneProject({ rootId: payload.rootId, url: payload.url, relativePath: payload.relativePath, requestId: payload.requestId });
36720
+ return this.cloneProject({
36721
+ rootId: payload.rootId,
36722
+ url: payload.url,
36723
+ relativePath: payload.relativePath,
36724
+ displayName: payload.displayName,
36725
+ color: payload.color,
36726
+ icon: payload.icon,
36727
+ requestId: payload.requestId
36728
+ });
36716
36729
  }
36717
36730
  if (command.type === "project.clone.cancel") {
36718
36731
  if (typeof this.cancelProjectClone !== "function") throw new Error("Remote clone cancellation is unavailable");
@@ -36984,6 +36997,7 @@ var require_mobile_runtime = __commonJS({
36984
36997
  agent: session.agent
36985
36998
  });
36986
36999
  const knownCount = Array.from(session.items.values()).filter(isConversationItem).length;
37000
+ const hydratedCount = ((_d = (_c = context.client) == null ? void 0 : _c.subscriptions) == null ? void 0 : _d.has(sessionId)) ? this._subscriptionSnapshot(session).items.filter(isConversationItem).length : 0;
36987
37001
  const page = pageConversationMessages(messages, {
36988
37002
  before: Number.isSafeInteger(payload.before) ? payload.before : void 0,
36989
37003
  anchor: payload.anchor && typeof payload.anchor === "object" ? {
@@ -36992,6 +37006,7 @@ var require_mobile_runtime = __commonJS({
36992
37006
  timestamp: cleanText(payload.anchor.timestamp, 64)
36993
37007
  } : null,
36994
37008
  knownCount: Number.isSafeInteger(payload.knownCount) ? payload.knownCount : knownCount,
37009
+ hydratedCount,
36995
37010
  limit: 30
36996
37011
  });
36997
37012
  const events = conversationMessagesToEvents(page.messages, {
@@ -37063,7 +37078,7 @@ var require_mobile_runtime = __commonJS({
37063
37078
  const value = typeof payload.value === "boolean" && REASONING_CONFIG_IDS.has(configId) ? payload.value : cleanText(payload.value, 200);
37064
37079
  if (value === null || value === "") throw new Error("Choose a configuration value");
37065
37080
  const session = this._session(sessionId);
37066
- if (((_c = session.currentTurn) == null ? void 0 : _c.state) === "running") {
37081
+ if (((_e = session.currentTurn) == null ? void 0 : _e.state) === "running") {
37067
37082
  throw new Error("Wait for the current response to finish");
37068
37083
  }
37069
37084
  return this.manager.setConfigOption(sessionId, configId, value);
@@ -37209,11 +37224,11 @@ var require_mobile_runtime = __commonJS({
37209
37224
  for (const uploadId of uploadIds) this.attachmentUploads.delete(uploadId);
37210
37225
  return { accepted: true, ...(delivered == null ? void 0 : delivered.turnId) ? { turnId: delivered.turnId } : {} };
37211
37226
  } catch (error) {
37212
- const localEcho = (_d = this.sessions.get(sessionId)) == null ? void 0 : _d.items.get(localEchoId);
37227
+ const localEcho = (_f = this.sessions.get(sessionId)) == null ? void 0 : _f.items.get(localEchoId);
37213
37228
  if (localEcho == null ? void 0 : localEcho.providerItemId) return { accepted: true };
37214
37229
  this._publishProviderEvent(sessionId, {
37215
37230
  eventId: crypto.randomUUID(),
37216
- provider: (_e = this.sessions.get(sessionId)) == null ? void 0 : _e.provider,
37231
+ provider: (_g = this.sessions.get(sessionId)) == null ? void 0 : _g.provider,
37217
37232
  type: "item.completed",
37218
37233
  executionOrigin: "main",
37219
37234
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -37223,7 +37238,7 @@ var require_mobile_runtime = __commonJS({
37223
37238
  status: "failed",
37224
37239
  localEcho: true,
37225
37240
  remoteCommand: true,
37226
- data: { text, attachments: ((_f = localEcho == null ? void 0 : localEcho.data) == null ? void 0 : _f.attachments) || [] }
37241
+ data: { text, attachments: ((_h = localEcho == null ? void 0 : localEcho.data) == null ? void 0 : _h.attachments) || [] }
37227
37242
  }
37228
37243
  });
37229
37244
  this.remoteTurnSessions.delete(sessionId);
@@ -38677,9 +38692,14 @@ var require_remote_runtime_client = __commonJS({
38677
38692
  "session.resume",
38678
38693
  "history.older",
38679
38694
  "projects.list",
38695
+ "project.directories.list",
38696
+ "project.update",
38680
38697
  "project.register",
38681
38698
  "project.clone",
38682
38699
  "project.clone.cancel",
38700
+ "project.git.availability",
38701
+ "project.icon.availability",
38702
+ "project.icon.generate",
38683
38703
  "project.unregister",
38684
38704
  "tasks.list",
38685
38705
  "task.create",
@@ -38708,6 +38728,7 @@ var require_remote_runtime_client = __commonJS({
38708
38728
  "coordination.transcript",
38709
38729
  "coordination.message",
38710
38730
  "projects.list",
38731
+ "project.directories.list",
38711
38732
  "tasks.list",
38712
38733
  "providers.list",
38713
38734
  "provider.login.describe",
@@ -38773,15 +38794,20 @@ var require_remote_runtime_client = __commonJS({
38773
38794
  assertPublicPayload(child, depth + 1);
38774
38795
  }
38775
38796
  }
38776
- function stripPathFields(value, depth = 0) {
38797
+ function stripPathFields(value, depth = 0, preserveRelativePaths = false) {
38777
38798
  if (depth > 40) throw new Error("Remote runtime payload is too deeply nested");
38778
- if (Array.isArray(value)) return value.map((item) => stripPathFields(item, depth + 1));
38799
+ if (Array.isArray(value)) return value.map((item) => stripPathFields(item, depth + 1, preserveRelativePaths));
38779
38800
  if (!value || typeof value !== "object") return value;
38780
38801
  const clean = {};
38781
38802
  for (const [key, child] of Object.entries(value)) {
38782
38803
  const normalized = normalizedKey(key);
38783
- if (PATH_KEYS.has(normalized) || normalized.endsWith("path")) continue;
38784
- clean[key] = stripPathFields(child, depth + 1);
38804
+ if (PATH_KEYS.has(normalized) || normalized.endsWith("path")) {
38805
+ if (preserveRelativePaths && (child === null || isSafeRelativePath(child))) {
38806
+ clean[key] = typeof child === "string" ? child.replace(/\\/g, "/") : null;
38807
+ }
38808
+ continue;
38809
+ }
38810
+ clean[key] = stripPathFields(child, depth + 1, preserveRelativePaths);
38785
38811
  }
38786
38812
  return clean;
38787
38813
  }
@@ -39326,7 +39352,7 @@ var require_remote_runtime_client = __commonJS({
39326
39352
  }
39327
39353
  if (command.payload !== void 0) {
39328
39354
  assertPublicPayload(command.payload);
39329
- assertPathlessCommand(command.payload, 0, command.type === "project.register" || command.type === "project.clone" || command.type.startsWith("workspace.files."));
39355
+ assertPathlessCommand(command.payload, 0, command.type === "project.directories.list" || command.type === "project.register" || command.type === "project.clone" || command.type.startsWith("workspace.files."));
39330
39356
  wire.payload = clone(command.payload);
39331
39357
  }
39332
39358
  return wire;
@@ -39453,9 +39479,8 @@ var require_remote_runtime_client = __commonJS({
39453
39479
  return;
39454
39480
  }
39455
39481
  if (message.kind === "credential.renewed") {
39456
- if (!this.connection || Number(message.accessExpiresAt) !== this.connection.accessExpiresAt) {
39457
- return this._protocolFailure(socket);
39458
- }
39482
+ const accessExpiresAt = Number(message.accessExpiresAt);
39483
+ if (!this.connection || !Number.isFinite(accessExpiresAt) || Math.abs(accessExpiresAt - this.connection.accessExpiresAt) > 2e3) return;
39459
39484
  if (this.renewTimer) clearTimeout(this.renewTimer);
39460
39485
  this.renewTimer = null;
39461
39486
  this._diagnostic("remote.credential_renewed");
@@ -39578,7 +39603,7 @@ var require_remote_runtime_client = __commonJS({
39578
39603
  if (this.runtimeOnline) this._sendRuntimeHello();
39579
39604
  }
39580
39605
  _handleRuntimeEnvelope(envelope) {
39581
- var _a, _b;
39606
+ var _a, _b, _c, _d, _e;
39582
39607
  let safe;
39583
39608
  let bytes;
39584
39609
  try {
@@ -39592,7 +39617,8 @@ var require_remote_runtime_client = __commonJS({
39592
39617
  throw new Error("Invalid runtime snapshot");
39593
39618
  }
39594
39619
  }
39595
- safe = stripPathFields(envelope);
39620
+ const commandType = envelope.kind === "command.result" ? (_c = (_b = (_a = this.pendingCommands.get(envelope.commandId)) == null ? void 0 : _a.message) == null ? void 0 : _b.command) == null ? void 0 : _c.type : null;
39621
+ safe = stripPathFields(envelope, 0, commandType === "project.directories.list");
39596
39622
  } catch {
39597
39623
  const kind = typeof (envelope == null ? void 0 : envelope.kind) === "string" && /^[a-z][a-z0-9.]{0,63}$/.test(envelope.kind) ? envelope.kind : void 0;
39598
39624
  this._diagnostic("remote.runtime_rejected", { reason: "invalid_envelope", kind });
@@ -39646,8 +39672,8 @@ var require_remote_runtime_client = __commonJS({
39646
39672
  totalMs: this.connectTrace ? this.now() - this.connectTrace.startedAt : void 0,
39647
39673
  reset: safe.reset === true,
39648
39674
  bytes,
39649
- sessions: Array.isArray((_a = safe.snapshot) == null ? void 0 : _a.sessions) ? safe.snapshot.sessions.length : void 0,
39650
- projects: Array.isArray((_b = safe.snapshot) == null ? void 0 : _b.projects) ? safe.snapshot.projects.length : void 0
39675
+ sessions: Array.isArray((_d = safe.snapshot) == null ? void 0 : _d.sessions) ? safe.snapshot.sessions.length : void 0,
39676
+ projects: Array.isArray((_e = safe.snapshot) == null ? void 0 : _e.projects) ? safe.snapshot.projects.length : void 0
39651
39677
  });
39652
39678
  this.connectTrace = null;
39653
39679
  this._emitEnvelope(safe);
@@ -40235,6 +40261,7 @@ var require_peer_runtime_network = __commonJS({
40235
40261
  var MAX_PEERS = 32;
40236
40262
  var PEER_ACCESS_TTL_MS = 7 * 24 * 60 * 6e4;
40237
40263
  var PEER_HANDSHAKE_TIMEOUT_MS = 5e3;
40264
+ var PEER_DENIAL_FEATURE = "peer-denial";
40238
40265
  function peerRef(value) {
40239
40266
  return value ? crypto.createHash("sha256").update(String(value)).digest("hex").slice(0, 10) : void 0;
40240
40267
  }
@@ -40273,7 +40300,18 @@ var require_peer_runtime_network = __commonJS({
40273
40300
  return;
40274
40301
  }
40275
40302
  if (message.kind === "runtime.message" && message.box) {
40276
- if (!this.network.relay.sendPeerMessage(this.peer.runtimeId, message.box, "to-runtime")) {
40303
+ let box = message.box;
40304
+ try {
40305
+ const payload = decryptJson(box, this.network.keyPair.secretKey, this.peer.publicKey);
40306
+ if ((payload == null ? void 0 : payload.kind) === "hello" && !(Array.isArray(payload.features) && payload.features.includes(PEER_DENIAL_FEATURE))) {
40307
+ box = encryptJson({
40308
+ ...payload,
40309
+ features: [...Array.isArray(payload.features) ? payload.features : [], PEER_DENIAL_FEATURE]
40310
+ }, this.network.keyPair.secretKey, this.peer.publicKey);
40311
+ }
40312
+ } catch {
40313
+ }
40314
+ if (!this.network.relay.sendPeerMessage(this.peer.runtimeId, box, "to-runtime")) {
40277
40315
  this.network._diagnostic("peer.route_failed", { peer: peerRef(this.peer.runtimeId), stream: "to-runtime" });
40278
40316
  this.offline();
40279
40317
  } else if (this.readyState === 1 && !this.handshakeComplete && !this.handshakeTimer) {
@@ -40283,11 +40321,17 @@ var require_peer_runtime_network = __commonJS({
40283
40321
  }
40284
40322
  receive(box) {
40285
40323
  if (this.readyState !== 1) return;
40324
+ let payload;
40286
40325
  try {
40287
- decryptJson(box, this.network.keyPair.secretKey, this.peer.publicKey);
40326
+ payload = decryptJson(box, this.network.keyPair.secretKey, this.peer.publicKey);
40288
40327
  this.network.relay.emit("diagnostic", { event: "peer.response_verified", stream: "to-client" });
40289
40328
  } catch {
40290
40329
  this.network.relay.emit("diagnostic", { event: "peer.response_rejected", reason: "decrypt_failed" });
40330
+ return;
40331
+ }
40332
+ if ((payload == null ? void 0 : payload.kind) === "peer.denied") {
40333
+ this.network._denyPeer(this.peer.runtimeId);
40334
+ return;
40291
40335
  }
40292
40336
  this.handshakeComplete = true;
40293
40337
  if (this.handshakeTimer) clearTimeout(this.handshakeTimer);
@@ -40327,6 +40371,7 @@ var require_peer_runtime_network = __commonJS({
40327
40371
  this.saveRosters = saveRosters;
40328
40372
  this.rosters = /* @__PURE__ */ new Map();
40329
40373
  this.revokedPeers = /* @__PURE__ */ new Set();
40374
+ this.deniedPeers = /* @__PURE__ */ new Set();
40330
40375
  this.peers = /* @__PURE__ */ new Map();
40331
40376
  this.clients = /* @__PURE__ */ new Map();
40332
40377
  this.clientSockets = /* @__PURE__ */ new Map();
@@ -40374,6 +40419,7 @@ var require_peer_runtime_network = __commonJS({
40374
40419
  publicKey: peer.publicKey,
40375
40420
  name: peer.name.trim().slice(0, 200) || "Connected host"
40376
40421
  }])).values()];
40422
+ for (const peer of unique) this.deniedPeers.delete(peer.runtimeId);
40377
40423
  if (unique.length) this.rosters.set(deviceId, unique);
40378
40424
  else this.rosters.delete(deviceId);
40379
40425
  for (const runtimeId of this.revokedPeers) {
@@ -40428,11 +40474,12 @@ var require_peer_runtime_network = __commonJS({
40428
40474
  const previous = this.peers.get(runtimeId);
40429
40475
  const current = next.get(runtimeId);
40430
40476
  if ((current == null ? void 0 : current.publicKey) === previous.publicKey) continue;
40477
+ this.deniedPeers.delete(runtimeId);
40431
40478
  this._removePeer(runtimeId);
40432
40479
  }
40433
40480
  this.peers = next;
40434
40481
  for (const peer of this.peers.values()) {
40435
- if (!this.clients.has(peer.runtimeId)) this._addPeer(peer);
40482
+ if (!this.deniedPeers.has(peer.runtimeId) && !this.clients.has(peer.runtimeId)) this._addPeer(peer);
40436
40483
  }
40437
40484
  this._notifyClients();
40438
40485
  }
@@ -40517,6 +40564,24 @@ var require_peer_runtime_network = __commonJS({
40517
40564
  this.serverSockets.delete(runtimeId);
40518
40565
  this._diagnostic("peer.removed", { peer: peerRef(runtimeId) });
40519
40566
  }
40567
+ _denyPeer(runtimeId) {
40568
+ if (!this.peers.has(runtimeId)) return;
40569
+ this.deniedPeers.add(runtimeId);
40570
+ this._removePeer(runtimeId);
40571
+ this._diagnostic("peer.connection_denied", { peer: peerRef(runtimeId) });
40572
+ this._notifyClients();
40573
+ }
40574
+ _rosterPeer(runtimeId) {
40575
+ let found = null;
40576
+ for (const roster of this.rosters.values()) {
40577
+ for (const peer of roster) {
40578
+ if (peer.runtimeId !== runtimeId) continue;
40579
+ if (found && found.publicKey !== peer.publicKey) return null;
40580
+ found = peer;
40581
+ }
40582
+ }
40583
+ return found;
40584
+ }
40520
40585
  _handleRelayEvent(event) {
40521
40586
  var _a, _b, _c;
40522
40587
  if ((event == null ? void 0 : event.kind) === "peer.online" && ID_PATTERN.test(event.targetRuntimeId || "")) {
@@ -40537,6 +40602,20 @@ var require_peer_runtime_network = __commonJS({
40537
40602
  if ((event == null ? void 0 : event.kind) !== "peer.message" || !ID_PATTERN.test(event.sourceRuntimeId || "") || !KEY_PATTERN.test(event.sourcePublicKey || "")) return;
40538
40603
  const peer = this.peers.get(event.sourceRuntimeId);
40539
40604
  if (!peer || peer.publicKey !== event.sourcePublicKey) {
40605
+ const revokedPeer = this.revokedPeers.has(event.sourceRuntimeId) ? this._rosterPeer(event.sourceRuntimeId) : null;
40606
+ if ((revokedPeer == null ? void 0 : revokedPeer.publicKey) === event.sourcePublicKey && event.stream === "to-runtime") {
40607
+ let payload2;
40608
+ try {
40609
+ payload2 = decryptJson(event.box, this.keyPair.secretKey, revokedPeer.publicKey);
40610
+ } catch {
40611
+ }
40612
+ if ((payload2 == null ? void 0 : payload2.kind) === "hello" && Array.isArray(payload2.features) && payload2.features.includes(PEER_DENIAL_FEATURE)) {
40613
+ const box = encryptJson({ kind: "peer.denied" }, this.keyPair.secretKey, revokedPeer.publicKey);
40614
+ if (this.relay.sendPeerMessage(revokedPeer.runtimeId, box, "to-client")) {
40615
+ this._diagnostic("peer.denial_sent", { peer: peerRef(revokedPeer.runtimeId) });
40616
+ }
40617
+ }
40618
+ }
40540
40619
  this._diagnostic("peer.message_rejected", {
40541
40620
  reason: peer ? "key_mismatch" : "not_introduced",
40542
40621
  peer: peerRef(event.sourceRuntimeId)
@@ -40730,7 +40809,7 @@ var require_headless_session_bridge = __commonJS({
40730
40809
  };
40731
40810
  }
40732
40811
  var HeadlessSessionBridge = class {
40733
- constructor({ runtime, remoteClient, peerRuntimeNetwork = null, dataPath, deliverMessage, createPairingLink = null, randomBytes = crypto.randomBytes } = {}) {
40812
+ constructor({ runtime, remoteClient, peerRuntimeNetwork = null, dataPath, deliverMessage, listLocalSessions = null, readLocalTranscript = null, createPairingLink = null, randomBytes = crypto.randomBytes } = {}) {
40734
40813
  if (!runtime || !remoteClient || typeof deliverMessage !== "function" || !path.isAbsolute(dataPath || "")) {
40735
40814
  throw new Error("CAS Cloud session bridge configuration is invalid");
40736
40815
  }
@@ -40739,6 +40818,8 @@ var require_headless_session_bridge = __commonJS({
40739
40818
  this.peerRuntimeNetwork = peerRuntimeNetwork;
40740
40819
  this.dataPath = dataPath;
40741
40820
  this.deliverMessage = deliverMessage;
40821
+ this.listLocalSessions = typeof listLocalSessions === "function" ? listLocalSessions : null;
40822
+ this.readLocalTranscript = typeof readLocalTranscript === "function" ? readLocalTranscript : null;
40742
40823
  this.createPairingLink = createPairingLink;
40743
40824
  this.adminToken = randomBytes(32).toString("hex");
40744
40825
  this.sessionSecret = randomBytes(32);
@@ -40917,7 +40998,7 @@ var require_headless_session_bridge = __commonJS({
40917
40998
  });
40918
40999
  }
40919
41000
  async _handle(request, response) {
40920
- var _a, _b, _c;
41001
+ var _a, _b, _c, _d, _e, _f, _g, _h;
40921
41002
  const url = new URL(request.url, "http://127.0.0.1");
40922
41003
  if (url.pathname.startsWith("/admin/")) {
40923
41004
  if (!safeBearer(request, this.adminToken)) return sendJson(response, 401, { error: "Unauthorized" });
@@ -40969,11 +41050,29 @@ var require_headless_session_bridge = __commonJS({
40969
41050
  payload: {}
40970
41051
  })
40971
41052
  })));
40972
- const sessions = results.flatMap((entry) => entry.status === "fulfilled" ? (({ state, result }) => (Array.isArray(result == null ? void 0 : result.sessions) ? result.sessions : []).slice(0, 100).flatMap((session) => session && typeof session.id === "string" && session.id.length <= 128 ? [{
41053
+ const localSessions = (((_b = (_a = this.listLocalSessions) == null ? void 0 : _a.call(this)) == null ? void 0 : _b.sessions) || []).flatMap((session) => session && typeof session.id === "string" && session.id.length <= 128 ? [{
41054
+ id: session.id,
41055
+ name: clip(session.name, 120),
41056
+ agent: clip(session.agent, 60),
41057
+ project: clip(session.project, 160),
41058
+ agent_id: clip(session.agent_id, 60),
41059
+ project_icon: clip(session.project_icon, 140),
41060
+ project_color: clip(session.project_color, 32),
41061
+ goal: clip(session.goal, 1200),
41062
+ activity: clip(session.activity, 500),
41063
+ status: clip(session.status, 80),
41064
+ surface: session.surface === "terminal" ? "terminal" : "chat",
41065
+ state: ["working", "needs_input"].includes(session.state) ? session.state : "idle",
41066
+ is_current: session.id === sourceSessionId
41067
+ }] : []);
41068
+ const remoteSessions = results.flatMap((entry) => entry.status === "fulfilled" ? (({ state, result }) => (Array.isArray(result == null ? void 0 : result.sessions) ? result.sessions : []).slice(0, 100).flatMap((session) => session && typeof session.id === "string" && session.id.length <= 128 ? [{
40973
41069
  id: encodeRemoteSessionId(state.runtime.id, session.id),
40974
41070
  name: clip(session.name, 120),
40975
41071
  agent: clip(session.agent, 60),
40976
41072
  project: clip(session.project, 160),
41073
+ agent_id: clip(session.agent_id, 60),
41074
+ project_icon: clip(session.project_icon, 140),
41075
+ project_color: clip(session.project_color, 32),
40977
41076
  goal: clip(session.goal, 1200),
40978
41077
  activity: clip(session.activity, 500),
40979
41078
  status: clip(session.status, 80),
@@ -40982,7 +41081,7 @@ var require_headless_session_bridge = __commonJS({
40982
41081
  host: state.runtime.name || "Paired host",
40983
41082
  is_current: false
40984
41083
  }] : []))(entry.value) : []);
40985
- return sendJson(response, 200, { sessions });
41084
+ return sendJson(response, 200, { sessions: [...localSessions, ...remoteSessions] });
40986
41085
  } catch (_) {
40987
41086
  return sendJson(response, 503, { error: "The paired Mac is offline" });
40988
41087
  }
@@ -40993,6 +41092,25 @@ var require_headless_session_bridge = __commonJS({
40993
41092
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 60) {
40994
41093
  return sendJson(response, 400, { error: "Conversation limit must be between 1 and 60" });
40995
41094
  }
41095
+ if (this._sourceSession(body.target_session_id)) {
41096
+ if (!this.readLocalTranscript) return sendJson(response, 503, { error: "Conversation reading is unavailable" });
41097
+ try {
41098
+ const result = await this.readLocalTranscript({ targetSessionId: body.target_session_id, limit });
41099
+ const snapshot = boundedConversationMessages(result == null ? void 0 : result.messages, { limit });
41100
+ return sendJson(response, 200, {
41101
+ session: {
41102
+ id: body.target_session_id,
41103
+ name: clip((_c = result == null ? void 0 : result.session) == null ? void 0 : _c.name, 120),
41104
+ agent: clip((_d = result == null ? void 0 : result.session) == null ? void 0 : _d.agent, 60),
41105
+ project: clip((_e = result == null ? void 0 : result.session) == null ? void 0 : _e.project, 160)
41106
+ },
41107
+ messages: snapshot.messages,
41108
+ truncated: (result == null ? void 0 : result.truncated) === true || snapshot.truncated
41109
+ });
41110
+ } catch (_) {
41111
+ return sendJson(response, 503, { error: "The conversation could not be read" });
41112
+ }
41113
+ }
40996
41114
  let target;
40997
41115
  try {
40998
41116
  target = decodeRemoteSessionId(body.target_session_id);
@@ -41011,9 +41129,9 @@ var require_headless_session_bridge = __commonJS({
41011
41129
  return sendJson(response, 200, {
41012
41130
  session: {
41013
41131
  id: body.target_session_id,
41014
- name: clip((_a = result == null ? void 0 : result.session) == null ? void 0 : _a.name, 120),
41015
- agent: clip((_b = result == null ? void 0 : result.session) == null ? void 0 : _b.agent, 60),
41016
- project: clip((_c = result == null ? void 0 : result.session) == null ? void 0 : _c.project, 160),
41132
+ name: clip((_f = result == null ? void 0 : result.session) == null ? void 0 : _f.name, 120),
41133
+ agent: clip((_g = result == null ? void 0 : result.session) == null ? void 0 : _g.agent, 60),
41134
+ project: clip((_h = result == null ? void 0 : result.session) == null ? void 0 : _h.project, 160),
41017
41135
  host: state.runtime.name || "Paired Mac"
41018
41136
  },
41019
41137
  messages: snapshot.messages,
@@ -41033,10 +41151,30 @@ var require_headless_session_bridge = __commonJS({
41033
41151
  this._pruneReplies();
41034
41152
  if (messageType === "response") {
41035
41153
  const pending = this.pendingReplies.get(body.reply_to_request_id);
41036
- if (!pending || pending.direction !== "incoming" || pending.sourceSessionId !== body.target_session_id || pending.targetSessionId !== sourceSessionId) {
41154
+ if (!pending || !["incoming", "local"].includes(pending.direction) || pending.sourceSessionId !== body.target_session_id || pending.targetSessionId !== sourceSessionId) {
41037
41155
  return sendJson(response, 409, { error: "The response does not match an active session request" });
41038
41156
  }
41039
41157
  const source2 = this._sourceSession(sourceSessionId);
41158
+ if (pending.direction === "local") {
41159
+ try {
41160
+ const result = await this.deliverMessage({
41161
+ targetSessionId: pending.sourceSessionId,
41162
+ sourceSessionId,
41163
+ sourceName: (source2 == null ? void 0 : source2.title) || `${(source2 == null ? void 0 : source2.agent) || "CAS Cloud"} session`,
41164
+ sourceAgent: (source2 == null ? void 0 : source2.agent) || (source2 == null ? void 0 : source2.provider) || "agent",
41165
+ message: body.message.trim(),
41166
+ messageType: "response",
41167
+ communicationRequestId: body.reply_to_request_id
41168
+ });
41169
+ this.pendingReplies.delete(body.reply_to_request_id);
41170
+ return sendJson(response, 200, {
41171
+ success: true,
41172
+ status: (result == null ? void 0 : result.status) === "queued" ? "queued" : "delivered"
41173
+ });
41174
+ } catch (_) {
41175
+ return sendJson(response, 503, { error: "The session could not receive the response" });
41176
+ }
41177
+ }
41040
41178
  if (!pending.reply({
41041
41179
  sourceSessionId: pending.replyTargetSessionId,
41042
41180
  targetSessionId: pending.remoteSourceSessionId,
@@ -41049,6 +41187,37 @@ var require_headless_session_bridge = __commonJS({
41049
41187
  this.pendingReplies.delete(body.reply_to_request_id);
41050
41188
  return sendJson(response, 200, { success: true, status: "delivered" });
41051
41189
  }
41190
+ if (body.target_session_id === sourceSessionId) {
41191
+ return sendJson(response, 400, { error: "A session cannot message itself" });
41192
+ }
41193
+ const source = this._sourceSession(sourceSessionId);
41194
+ if (this._sourceSession(body.target_session_id)) {
41195
+ const communicationRequestId2 = crypto.randomUUID();
41196
+ this._rememberReply(communicationRequestId2, {
41197
+ direction: "local",
41198
+ sourceSessionId,
41199
+ targetSessionId: body.target_session_id
41200
+ });
41201
+ try {
41202
+ const result = await this.deliverMessage({
41203
+ targetSessionId: body.target_session_id,
41204
+ sourceSessionId,
41205
+ sourceName: (source == null ? void 0 : source.title) || `${(source == null ? void 0 : source.agent) || "CAS Cloud"} session`,
41206
+ sourceAgent: (source == null ? void 0 : source.agent) || (source == null ? void 0 : source.provider) || "agent",
41207
+ message: body.message.trim(),
41208
+ messageType: "request",
41209
+ communicationRequestId: communicationRequestId2
41210
+ });
41211
+ return sendJson(response, 200, {
41212
+ success: true,
41213
+ status: (result == null ? void 0 : result.status) === "queued" ? "queued" : "delivered",
41214
+ request_id: communicationRequestId2
41215
+ });
41216
+ } catch (_) {
41217
+ this.pendingReplies.delete(communicationRequestId2);
41218
+ return sendJson(response, 503, { error: "The session could not receive the request" });
41219
+ }
41220
+ }
41052
41221
  let target;
41053
41222
  try {
41054
41223
  target = decodeRemoteSessionId(body.target_session_id);
@@ -41056,7 +41225,6 @@ var require_headless_session_bridge = __commonJS({
41056
41225
  return sendJson(response, 400, { error: error.message });
41057
41226
  }
41058
41227
  if (!this._remoteClient(target.runtimeId)) return sendJson(response, 404, { error: "The remote session is unavailable" });
41059
- const source = this._sourceSession(sourceSessionId);
41060
41228
  const communicationRequestId = crypto.randomUUID();
41061
41229
  this._rememberReply(communicationRequestId, {
41062
41230
  direction: "outgoing",
@@ -41398,7 +41566,8 @@ var require_headless_project_registry = __commonJS({
41398
41566
  onOperation = () => {
41399
41567
  },
41400
41568
  maxConcurrentClones = MAX_CLONES,
41401
- stopTimeoutMs = 2e3
41569
+ stopTimeoutMs = 2e3,
41570
+ platform = process.platform
41402
41571
  }) {
41403
41572
  if (!(database == null ? void 0 : database.db)) throw new Error("HeadlessProjectRegistry requires a SQLite database");
41404
41573
  this.database = database;
@@ -41409,6 +41578,7 @@ var require_headless_project_registry = __commonJS({
41409
41578
  this.onOperation = onOperation;
41410
41579
  this.maxConcurrentClones = maxConcurrentClones;
41411
41580
  this.stopTimeoutMs = stopTimeoutMs;
41581
+ this.platform = platform;
41412
41582
  this.stopping = false;
41413
41583
  this.running = /* @__PURE__ */ new Map();
41414
41584
  this.queue = [];
@@ -41527,6 +41697,19 @@ var require_headless_project_registry = __commonJS({
41527
41697
  const result = this._registerPath(resolved, root ? this._rootByPath(root.rootPath).root_id : null, { legacy: true });
41528
41698
  if (result.changed) changed = true;
41529
41699
  }
41700
+ for (const row of this.db.prepare("SELECT project_id, path FROM runtime_projects WHERE registered = 1").all()) {
41701
+ let resolved;
41702
+ try {
41703
+ resolved = fs.realpathSync(row.path);
41704
+ } catch (_) {
41705
+ continue;
41706
+ }
41707
+ if (resolved === row.path) continue;
41708
+ const canonical = this.db.prepare("SELECT project_id FROM runtime_projects WHERE path = ? AND registered = 1").get(resolved);
41709
+ if (!canonical || canonical.project_id === row.project_id) continue;
41710
+ this.db.prepare("UPDATE runtime_projects SET registered = 0, updated_at = CURRENT_TIMESTAMP WHERE project_id = ?").run(row.project_id);
41711
+ changed = true;
41712
+ }
41530
41713
  return changed;
41531
41714
  }
41532
41715
  _resolveDirectory(candidate, label) {
@@ -41588,7 +41771,7 @@ var require_headless_project_registry = __commonJS({
41588
41771
  throw runtimeError("clone_root_insecure", "The configured clone root is not secure");
41589
41772
  }
41590
41773
  const effectiveUid = typeof process.geteuid === "function" ? process.geteuid() : null;
41591
- if (!stat.isDirectory() || effectiveUid !== null && stat.uid !== effectiveUid || (stat.mode & 18) !== 0) {
41774
+ if (!stat.isDirectory() || this.platform !== "win32" && (effectiveUid !== null && stat.uid !== effectiveUid || (stat.mode & 18) !== 0)) {
41592
41775
  throw runtimeError("clone_root_insecure", "The configured clone root is not secure");
41593
41776
  }
41594
41777
  return stat;
@@ -41635,6 +41818,46 @@ var require_headless_project_registry = __commonJS({
41635
41818
  getRoots() {
41636
41819
  return this.db.prepare("SELECT root_id, name FROM runtime_project_roots ORDER BY created_at, root_id").all().map((root) => ({ rootId: root.root_id, name: String(root.name).slice(0, 200) }));
41637
41820
  }
41821
+ gitAvailability() {
41822
+ return new Promise((resolve) => {
41823
+ var _a, _b;
41824
+ let child;
41825
+ let output = "";
41826
+ let settled = false;
41827
+ const finish = (result) => {
41828
+ if (settled) return;
41829
+ settled = true;
41830
+ clearTimeout(timer);
41831
+ resolve(result);
41832
+ };
41833
+ const timer = setTimeout(() => {
41834
+ try {
41835
+ child == null ? void 0 : child.kill();
41836
+ } catch (_) {
41837
+ }
41838
+ finish({ available: false });
41839
+ }, 2e3);
41840
+ (_a = timer.unref) == null ? void 0 : _a.call(timer);
41841
+ try {
41842
+ child = this.spawnImpl("git", ["--version"], {
41843
+ shell: false,
41844
+ stdio: ["ignore", "pipe", "ignore"],
41845
+ windowsHide: true
41846
+ });
41847
+ (_b = child.stdout) == null ? void 0 : _b.on("data", (chunk) => {
41848
+ output = `${output}${String(chunk)}`.slice(0, 200);
41849
+ });
41850
+ child.once("error", () => finish({ available: false }));
41851
+ child.once("close", (code) => {
41852
+ var _a2;
41853
+ const version = (_a2 = /^git version\s+([^\s]+)/i.exec(output.trim())) == null ? void 0 : _a2[1];
41854
+ finish(code === 0 ? { available: true, ...version ? { version } : {} } : { available: false });
41855
+ });
41856
+ } catch (_) {
41857
+ finish({ available: false });
41858
+ }
41859
+ });
41860
+ }
41638
41861
  listDirectories({ rootId, relativePath = "." } = {}) {
41639
41862
  const root = this._root(rootId);
41640
41863
  const currentPath = validateRelativePath(relativePath || ".");
@@ -41794,12 +42017,20 @@ var require_headless_project_registry = __commonJS({
41794
42017
  const revision = this._bumpRevision();
41795
42018
  return this._recordRequest(requestId, hash, { projectId: project.projectId, revision, updated: true });
41796
42019
  }
41797
- clone({ rootId, url, relativePath, requestId }) {
42020
+ clone({ rootId, url, relativePath, displayName, color, icon, requestId }) {
41798
42021
  const normalizedUrl = validateGitUrl(url);
41799
42022
  const root = this._root(rootId);
41800
42023
  this._assertSecureCloneRoot(root);
41801
42024
  const normalizedRelative = validateCloneChildName(relativePath || cloneDirectoryName(normalizedUrl));
41802
- const hash = requestHash("clone", { rootId, url: normalizedUrl, relativePath: normalizedRelative });
42025
+ const appearance = {
42026
+ ...displayName !== void 0 ? { displayName: String(displayName || "").trim().slice(0, 120) } : {},
42027
+ ...color !== void 0 ? { color: String(color || "").trim().slice(0, 32) } : {},
42028
+ ...icon !== void 0 ? { icon } : {}
42029
+ };
42030
+ if (appearance.displayName !== void 0 && !appearance.displayName || appearance.icon !== void 0 && appearance.icon !== null && (typeof appearance.icon !== "string" || !ICON_PATTERN.test(appearance.icon))) {
42031
+ throw runtimeError("invalid_project_update", "Project changes are invalid");
42032
+ }
42033
+ const hash = requestHash("clone", { rootId, url: normalizedUrl, relativePath: normalizedRelative, ...appearance });
41803
42034
  const duplicate = this._request(requestId, hash);
41804
42035
  if (duplicate) return duplicate;
41805
42036
  const destinationInfo = this._cloneDestination(root, normalizedRelative, normalizedUrl);
@@ -41816,6 +42047,7 @@ var require_headless_project_registry = __commonJS({
41816
42047
  requestId,
41817
42048
  rootId,
41818
42049
  url: normalizedUrl,
42050
+ appearance,
41819
42051
  destination: destinationInfo.destination,
41820
42052
  temporaryDestination: null,
41821
42053
  temporaryIdentity: null,
@@ -41953,7 +42185,17 @@ var require_headless_project_registry = __commonJS({
41953
42185
  return this._finish(operation, "failed", "path_symlink_escape");
41954
42186
  }
41955
42187
  const registered = this._registerPath(resolved, root.root_id);
41956
- const revision = registered.changed ? this._bumpRevision() : this.getRevision();
42188
+ if (Object.keys(operation.appearance).length) {
42189
+ this.db.prepare(`UPDATE runtime_projects
42190
+ SET display_name = COALESCE(?, display_name), color = COALESCE(?, color), icon = ?, updated_at = CURRENT_TIMESTAMP
42191
+ WHERE project_id = ?`).run(
42192
+ operation.appearance.displayName ?? null,
42193
+ operation.appearance.color ?? null,
42194
+ operation.appearance.icon === void 0 ? registered.row.icon || null : operation.appearance.icon,
42195
+ registered.row.project_id
42196
+ );
42197
+ }
42198
+ const revision = registered.changed || Object.keys(operation.appearance).length ? this._bumpRevision() : this.getRevision();
41957
42199
  this.db.prepare(`UPDATE runtime_project_operations SET state = 'succeeded', project_id = ?, error = NULL, updated_at = CURRENT_TIMESTAMP WHERE operation_id = ?`).run(registered.row.project_id, operation.operationId);
41958
42200
  this.running.delete(operation.operationId);
41959
42201
  this.destinations.delete(operation.destination);
@@ -42035,6 +42277,7 @@ var require_headless_project_registry = __commonJS({
42035
42277
  contained,
42036
42278
  runtimeError,
42037
42279
  validateGitUrl,
42280
+ cloneDirectoryName,
42038
42281
  validateCloneChildName,
42039
42282
  validateRelativePath
42040
42283
  };
@@ -49241,6 +49484,8 @@ var require_headless_runtime = __commonJS({
49241
49484
  var os = require("os");
49242
49485
  var path = require("path");
49243
49486
  var { DriverChatManager, SESSION_EVENT } = require_driver_chat_manager();
49487
+ var { CHAT_PERMISSION_MODES } = require_chat_permission_modes();
49488
+ var { classifyProviderStartupError } = require_provider_auth();
49244
49489
  var { boundedConversationMessages } = require_chat_history_pagination();
49245
49490
  var { MobileRuntime } = require_mobile_runtime();
49246
49491
  var { MobileRelayClient } = require_mobile_relay_client();
@@ -49290,6 +49535,7 @@ var require_headless_runtime = __commonJS({
49290
49535
  "project.register",
49291
49536
  "project.clone",
49292
49537
  "project.clone.cancel",
49538
+ "project.git.availability",
49293
49539
  "project.unregister",
49294
49540
  "shortcuts.manage",
49295
49541
  "session.action",
@@ -49314,18 +49560,11 @@ var require_headless_runtime = __commonJS({
49314
49560
  "workspace.git.switch",
49315
49561
  "workspace.git.create"
49316
49562
  ]);
49317
- var FINAL_COORDINATION_STATUSES = /* @__PURE__ */ new Set(["done", "pushed", "completed", "finished"]);
49318
- var COORDINATION_IDLE_MS = 30 * 6e4;
49319
49563
  var COORDINATION_COMPLETION_GRACE_MS = 5e3;
49320
- function isCoordinatedSessionEligible(session, now = Date.now()) {
49321
- var _a, _b, _c;
49322
- if (!session || session.state === "stopped" || typeof session.terminalUuid !== "string" || !session.terminalUuid) return false;
49323
- const status = typeof session.workStatus === "string" ? session.workStatus.trim().toLowerCase() : "";
49324
- if (FINAL_COORDINATION_STATUSES.has(status)) return false;
49325
- if (((_a = session.currentTurn) == null ? void 0 : _a.state) === "running" || status === "needs_input" || status === "working") return true;
49326
- const lastActivity = Number(session.lastActivityAt) || Date.parse(session.lastActivityAt);
49327
- if (Number.isFinite(lastActivity) && now - lastActivity > COORDINATION_IDLE_MS) return false;
49328
- return Array.from(((_c = (_b = session.items) == null ? void 0 : _b.values) == null ? void 0 : _c.call(_b)) || []).some((item) => (item == null ? void 0 : item.itemType) === "user_message" || (item == null ? void 0 : item.itemType) === "assistant_message");
49564
+ function isCoordinatedSessionEligible(session) {
49565
+ return Boolean(
49566
+ session && session.state !== "stopped" && typeof session.terminalUuid === "string" && session.terminalUuid
49567
+ );
49329
49568
  }
49330
49569
  function appDataPath({ env = process.env, platform = process.platform, home = os.homedir() } = {}) {
49331
49570
  if (platform === "linux") {
@@ -49581,7 +49820,8 @@ var require_headless_runtime = __commonJS({
49581
49820
  quotaService: suppliedQuotaService = null,
49582
49821
  generateConversationTitle: generateConversationTitleFn = generateConversationTitle,
49583
49822
  forkConversation: forkConversationFn = forkConversation,
49584
- spawnImpl
49823
+ spawnImpl,
49824
+ getuid = typeof process.getuid === "function" ? () => process.getuid() : null
49585
49825
  } = {}) {
49586
49826
  if (typeof getToken !== "function") throw new Error("CAS CLI requires an access token provider");
49587
49827
  if (!validIdentity(identity)) throw new Error("CAS CLI identity is invalid");
@@ -49623,7 +49863,17 @@ var require_headless_runtime = __commonJS({
49623
49863
  const startSessionWithPreferences = async (options) => {
49624
49864
  const launchOptions = chatPreferences.apply(options.agent, options);
49625
49865
  if (launchOptions.permissionMode === void 0) launchOptions.permissionMode = "default";
49626
- const started = await manager.startSession(launchOptions);
49866
+ if (options.agent === "claude" && launchOptions.permissionMode === CHAT_PERMISSION_MODES.FULL_ACCESS && (getuid == null ? void 0 : getuid()) === 0) launchOptions.permissionMode = "default";
49867
+ let started;
49868
+ try {
49869
+ started = await manager.startSession(launchOptions);
49870
+ } catch (error) {
49871
+ const status = classifyProviderStartupError(options.agent, error);
49872
+ if (status) {
49873
+ error.code = status.state === "not_installed" ? "provider_not_installed" : "provider_unauthenticated";
49874
+ }
49875
+ throw error;
49876
+ }
49627
49877
  chatPreferences.write(started.agent || options.agent, {
49628
49878
  permissionMode: started.permissionMode,
49629
49879
  effort: started.effort
@@ -49769,17 +50019,20 @@ var require_headless_runtime = __commonJS({
49769
50019
  };
49770
50020
  const listCoordinatedSessions = () => ({
49771
50021
  sessions: Array.from(runtime.sessions.values()).flatMap((session) => {
49772
- var _a, _b;
50022
+ var _a, _b, _c, _d;
49773
50023
  return isCoordinatedSessionEligible(session) ? [{
49774
50024
  id: session.terminalUuid,
49775
50025
  name: session.title || `${session.agent || "Agent"} session`,
49776
50026
  agent: session.agent || session.provider || "",
50027
+ agent_id: session.agent || session.provider || "",
49777
50028
  project: ((_a = session.project) == null ? void 0 : _a.name) || "",
50029
+ project_icon: ((_b = session.project) == null ? void 0 : _b.icon) || "",
50030
+ project_color: ((_c = session.project) == null ? void 0 : _c.color) || "",
49778
50031
  goal: session.goal || "",
49779
50032
  activity: session.activity || "",
49780
50033
  status: session.workStatus || "",
49781
50034
  surface: "chat",
49782
- state: session.workStatus === "needs_input" ? "needs_input" : ((_b = session.currentTurn) == null ? void 0 : _b.state) === "running" ? "working" : "idle"
50035
+ state: session.workStatus === "needs_input" ? "needs_input" : ((_d = session.currentTurn) == null ? void 0 : _d.state) === "running" ? "working" : "idle"
49783
50036
  }] : [];
49784
50037
  }).slice(0, 100)
49785
50038
  });
@@ -50053,6 +50306,7 @@ ${message}`;
50053
50306
  registerProject: (payload) => registry.register(payload),
50054
50307
  cloneProject: (payload) => registry.clone(payload),
50055
50308
  cancelProjectClone: (payload) => registry.cancelClone(payload),
50309
+ gitAvailability: () => registry.gitAvailability(),
50056
50310
  unregisterProject: (payload) => {
50057
50311
  const busy = startingProjects.has(payload.projectId) || Array.from(runtime.sessions.values()).some((session) => {
50058
50312
  var _a;
@@ -50129,6 +50383,8 @@ ${message}`;
50129
50383
  peerRuntimeNetwork,
50130
50384
  dataPath: resolvedDataPath,
50131
50385
  deliverMessage: deliverCoordinatedMessage,
50386
+ listLocalSessions: listCoordinatedSessions,
50387
+ readLocalTranscript: readCoordinatedTranscript,
50132
50388
  createPairingLink: async () => {
50133
50389
  const pairing = await relay.createPairing();
50134
50390
  return { url: desktopConnectionLink(pairing), expiresAt: pairing.expiresAt };
@@ -50175,7 +50431,7 @@ ${message}`;
50175
50431
  }
50176
50432
  terminalOrder = Math.max(terminalOrder, saved.terminalOrder || 0);
50177
50433
  try {
50178
- const started = await manager.startSession({
50434
+ const started = await startSessionWithPreferences({
50179
50435
  agent: saved.agent,
50180
50436
  ...saved.accountId && saved.accountId !== "current" ? { accountId: saved.accountId } : {},
50181
50437
  cwd: saved.cwd,
@@ -50586,6 +50842,9 @@ var require_mobile_pairing_ipc = __commonJS({
50586
50842
  "use strict";
50587
50843
  var MOBILE_PAIRING_EVENT = "mobile-pairing:event";
50588
50844
  var { PRODUCTION_MOBILE_WEB_ORIGIN } = require_mobile_build_channel();
50845
+ var WARM_PAIRING_MIN_REMAINING_MS = 2 * 6e4;
50846
+ var WARM_PAIRING_CHECK_INTERVAL_MS = 3e4;
50847
+ var WARM_PAIRING_SERVE_MIN_REMAINING_MS = 15e3;
50589
50848
  function pairingPayload(pairing, mobileWebOrigin = PRODUCTION_MOBILE_WEB_ORIGIN) {
50590
50849
  const credentials = new URL("codeagentswarm://pair");
50591
50850
  credentials.searchParams.set("relay", new URL(pairing.relayOrigin).origin);
@@ -50607,13 +50866,49 @@ var require_mobile_pairing_ipc = __commonJS({
50607
50866
  setKeepAvailable = () => false,
50608
50867
  createQrDataUrl = (payload, options) => require("qrcode").toDataURL(payload, options)
50609
50868
  }) {
50869
+ var _a;
50610
50870
  const broadcast = (message) => {
50611
50871
  for (const window2 of BrowserWindow.getAllWindows()) {
50612
50872
  if (!window2.isDestroyed()) window2.webContents.send(MOBILE_PAIRING_EVENT, message);
50613
50873
  }
50614
50874
  };
50615
- relayClient.on("status", (status) => broadcast({ type: "status", status }));
50616
- relayClient.on("event", (event) => broadcast({ type: "relay", event }));
50875
+ const buildPairing = async () => {
50876
+ const pairing = await relayClient.createPairing();
50877
+ const qrDataUrl = await createQrDataUrl(
50878
+ pairingPayload(pairing, mobileWebOrigin),
50879
+ {
50880
+ scale: 8,
50881
+ margin: 4,
50882
+ errorCorrectionLevel: "L",
50883
+ color: { dark: "#111827", light: "#ffffff" }
50884
+ }
50885
+ );
50886
+ return { expiresAt: pairing.expiresAt, pairingCode: pairing.pairingCode, qrDataUrl };
50887
+ };
50888
+ let warm = null;
50889
+ let warming = null;
50890
+ const warmPairing = () => {
50891
+ if (warming || relayClient.status !== "online") return warming;
50892
+ const remaining = warm ? warm.expiresAt - Date.now() : 0;
50893
+ if (warm && (warm.served ? remaining > 0 : remaining > WARM_PAIRING_MIN_REMAINING_MS)) return null;
50894
+ warming = buildPairing().then((result) => {
50895
+ warm = result;
50896
+ }, () => {
50897
+ }).finally(() => {
50898
+ warming = null;
50899
+ });
50900
+ return warming;
50901
+ };
50902
+ const warmTimer = setInterval(warmPairing, WARM_PAIRING_CHECK_INTERVAL_MS);
50903
+ (_a = warmTimer.unref) == null ? void 0 : _a.call(warmTimer);
50904
+ relayClient.on("status", (status) => {
50905
+ broadcast({ type: "status", status });
50906
+ if ((status == null ? void 0 : status.status) === "online") void warmPairing();
50907
+ });
50908
+ relayClient.on("event", (event) => {
50909
+ if ((event == null ? void 0 : event.kind) === "pair.scanned") warm = null;
50910
+ broadcast({ type: "relay", event });
50911
+ });
50617
50912
  ipcMain.handle("mobile-pairing:status", () => ({
50618
50913
  success: true,
50619
50914
  ...relayClient.getStatus(),
@@ -50645,23 +50940,16 @@ var require_mobile_pairing_ipc = __commonJS({
50645
50940
  ipcMain.handle("mobile-pairing:create", async () => {
50646
50941
  try {
50647
50942
  if (getKeepAvailable() == null) setKeepAvailable(true);
50648
- const pairing = await relayClient.createPairing();
50649
- const qrDataUrl = await createQrDataUrl(
50650
- pairingPayload(pairing, mobileWebOrigin),
50651
- {
50652
- scale: 8,
50653
- margin: 4,
50654
- errorCorrectionLevel: "L",
50655
- color: { dark: "#111827", light: "#ffffff" }
50656
- }
50657
- );
50943
+ if (warming) await warming;
50944
+ if (!warm || warm.expiresAt - Date.now() < WARM_PAIRING_SERVE_MIN_REMAINING_MS) warm = await buildPairing();
50945
+ warm.served = true;
50658
50946
  return {
50659
50947
  success: true,
50660
- expiresAt: pairing.expiresAt,
50661
- pairingCode: pairing.pairingCode,
50948
+ expiresAt: warm.expiresAt,
50949
+ pairingCode: warm.pairingCode,
50662
50950
  webUrl: mobileWebOrigin,
50663
50951
  keepAvailable: getKeepAvailable() === true,
50664
- qrDataUrl
50952
+ qrDataUrl: warm.qrDataUrl
50665
50953
  };
50666
50954
  } catch (error) {
50667
50955
  return { success: false, error: error.message };
@@ -50707,7 +50995,7 @@ var require_cas = __commonJS({
50707
50995
  loadIdentity,
50708
50996
  resolveProject
50709
50997
  } = require_headless_runtime();
50710
- var version = true ? "0.0.12" : JSON.parse(fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf8")).version;
50998
+ var version = true ? "0.0.16" : JSON.parse(fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf8")).version;
50711
50999
  function help() {
50712
51000
  return `CAS CLI ${version}
50713
51001