@p4code/cli 0.0.51 → 0.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/bin.mjs +335 -64
  2. package/dist/client/assets/{DiffPanel-4ZPfw-bt.js → DiffPanel-AiSRvjRX.js} +2 -2
  3. package/dist/client/assets/{FilePreviewPanel-DPl3V4qz.js → FilePreviewPanel-MXaLfL5p.js} +2 -2
  4. package/dist/client/assets/{PreviewPanel-DENxqydi.js → PreviewPanel-Bi6HCJsd.js} +2 -2
  5. package/dist/client/assets/{angular-html-SfFqn_n1.js → angular-html-D1bodirL.js} +2 -2
  6. package/dist/client/assets/{angular-ts-TqTMcSgN.js → angular-ts-DFwSl5dY.js} +2 -2
  7. package/dist/client/assets/{apl-DvTVLZcC.js → apl-BSgDHbhR.js} +2 -2
  8. package/dist/client/assets/arrow-right-Cv0-U4r_.js +2 -0
  9. package/dist/client/assets/{astro-DEpzE1sb.js → astro-B5xAVlWW.js} +2 -2
  10. package/dist/client/assets/{blade-Brgi-xhV.js → blade-DWwZA3Xz.js} +2 -2
  11. package/dist/client/assets/{c-7H0fCJJT.js → c-BEpb31ei.js} +2 -2
  12. package/dist/client/assets/{cobol-Cq_E3Ld9.js → cobol-C6zTWyM4.js} +2 -2
  13. package/dist/client/assets/{coffee-DpUX5Xcz.js → coffee-DdF9Yam1.js} +2 -2
  14. package/dist/client/assets/{cpp-BEjZmHLc.js → cpp-DKWi6_r-.js} +2 -2
  15. package/dist/client/assets/{crystal-CycEJw4X.js → crystal-DgkDMi80.js} +2 -2
  16. package/dist/client/assets/{css-y3Oj4fDi.js → css-Bwoot0jv.js} +2 -2
  17. package/dist/client/assets/{edge-DIFYHFsA.js → edge-DpISuE7V.js} +2 -2
  18. package/dist/client/assets/{elixir-raLzjUMQ.js → elixir-C01RZc83.js} +2 -2
  19. package/dist/client/assets/{elm-CZsXcOy1.js → elm-Bf8f9rUd.js} +2 -2
  20. package/dist/client/assets/{erb-DhXjIste.js → erb-CIf6pEnP.js} +2 -2
  21. package/dist/client/assets/{fileCommentAnnotations-DkadV_kZ.js → fileCommentAnnotations-DGiHJEB9.js} +2 -2
  22. package/dist/client/assets/{git-rebase-CZvi6-AX.js → git-rebase-BvUdIX_d.js} +2 -2
  23. package/dist/client/assets/{glimmer-js-N_UF0Pmi.js → glimmer-js-D81v3RPP.js} +2 -2
  24. package/dist/client/assets/{glimmer-ts-Cxd1tQNi.js → glimmer-ts-fNKMT61k.js} +2 -2
  25. package/dist/client/assets/{glsl-B8RUupmi.js → glsl-vja7cLas.js} +2 -2
  26. package/dist/client/assets/{graphql-Cz2mi8GF.js → graphql-BAZB24Xe.js} +2 -2
  27. package/dist/client/assets/{hack-DD3q-ECT.js → hack-CMtrp-V5.js} +2 -2
  28. package/dist/client/assets/{haml-C75rIu1p.js → haml-DBWpAY-o.js} +2 -2
  29. package/dist/client/assets/{handlebars-C1zz9h1i.js → handlebars-XK5zfRVr.js} +2 -2
  30. package/dist/client/assets/{html-derivative-HEAA-Z2a.js → html-derivative-Cw9C5kli.js} +2 -2
  31. package/dist/client/assets/{html-mXOatteF.js → html-vebb6dVP.js} +2 -2
  32. package/dist/client/assets/{http-DO0ZHyk0.js → http-BYvuKZHX.js} +2 -2
  33. package/dist/client/assets/{hurl-DyDQ6Iuv.js → hurl-CpsMR6xa.js} +2 -2
  34. package/dist/client/assets/{index-30avYZEg.js → index-DS3IY5mU.js} +196 -195
  35. package/dist/client/assets/{java-DicQ_x8H.js → java-RQhZQoJi.js} +2 -2
  36. package/dist/client/assets/{javascript-CTx-kiCR.js → javascript-zuHlOJ3o.js} +2 -2
  37. package/dist/client/assets/{jinja-ERQPZ9pQ.js → jinja-D85yp_cm.js} +2 -2
  38. package/dist/client/assets/{jison-BEedW5zW.js → jison-BrwNTiOp.js} +2 -2
  39. package/dist/client/assets/{json-MMdgCPnh.js → json-D6zKnh4D.js} +2 -2
  40. package/dist/client/assets/{jsx-CJ--w_e-.js → jsx-pJNfQcG9.js} +2 -2
  41. package/dist/client/assets/{julia-BvGAE4O0.js → julia-DciUhiLe.js} +2 -2
  42. package/dist/client/assets/{just-BvBn61hL.js → just-BEjxPLNv.js} +2 -2
  43. package/dist/client/assets/{latex-P2_b6CEl.js → latex-DsAADVlo.js} +2 -2
  44. package/dist/client/assets/{liquid-Dm2xzeBN.js → liquid-iJJrReic.js} +2 -2
  45. package/dist/client/assets/{lua-DYQLFKMA.js → lua-BI7-JqfY.js} +2 -2
  46. package/dist/client/assets/{marko-DCc0fplW.js → marko-EIBrVtPm.js} +2 -2
  47. package/dist/client/assets/{mdc-BI6uqWRL.js → mdc-Ck49TBkx.js} +2 -2
  48. package/dist/client/assets/{nginx-ijGdX1bh.js → nginx-DopLa0xS.js} +2 -2
  49. package/dist/client/assets/{nim-CUp-JGxu.js → nim-COE1mnjh.js} +2 -2
  50. package/dist/client/assets/{perl-BKpwnNRr.js → perl-D7l0Da76.js} +2 -2
  51. package/dist/client/assets/{php-DDStFT9W.js → php-BCE4oDfX.js} +2 -2
  52. package/dist/client/assets/{pug-HXcSxf00.js → pug-B_c7OAy1.js} +2 -2
  53. package/dist/client/assets/{qml-CbzgrBYo.js → qml-k34ZhWfO.js} +2 -2
  54. package/dist/client/assets/{r-CgMrZP2h.js → r-wgJNeMVi.js} +2 -2
  55. package/dist/client/assets/{razor-0ZAHbKMz.js → razor-JbycVmC6.js} +2 -2
  56. package/dist/client/assets/{regexp-Bzi1hKAb.js → regexp-7QFekdWt.js} +2 -2
  57. package/dist/client/assets/{rst-qWyAhT77.js → rst-C8gOEuzM.js} +2 -2
  58. package/dist/client/assets/{ruby-LCEp4zKA.js → ruby-DfZNo4kq.js} +2 -2
  59. package/dist/client/assets/{sas-BFhxcd3b.js → sas-BJhOgW_o.js} +2 -2
  60. package/dist/client/assets/{scss-BK0-84uz.js → scss-D926pszU.js} +2 -2
  61. package/dist/client/assets/{shellscript-CyZVCH9y.js → shellscript-CoqnDwms.js} +2 -2
  62. package/dist/client/assets/{shellsession-BMyVGgke.js → shellsession-CGMS8gZk.js} +2 -2
  63. package/dist/client/assets/{soy-C5EqpU05.js → soy-CIKF2D6I.js} +2 -2
  64. package/dist/client/assets/{sql-D51Kht_s.js → sql-CkVOyoyS.js} +2 -2
  65. package/dist/client/assets/{stata-ClgcFk9O.js → stata-CECQ179z.js} +2 -2
  66. package/dist/client/assets/{surrealql-DcFIgIgS.js → surrealql-Bf1Az_cR.js} +2 -2
  67. package/dist/client/assets/{svelte-5uir_iV_.js → svelte-BpIBHbiM.js} +2 -2
  68. package/dist/client/assets/{templ-IHhNUhu5.js → templ-DtxTEUC-.js} +2 -2
  69. package/dist/client/assets/{tex-DbjEQBr6.js → tex-CNvbX85C.js} +2 -2
  70. package/dist/client/assets/textarea-Da1Z5UPN.js +46 -0
  71. package/dist/client/assets/{ts-tags-xML8Swir.js → ts-tags-B2_hHKj2.js} +2 -2
  72. package/dist/client/assets/{tsx-BwMUW3gO.js → tsx-DNp5WCP5.js} +2 -2
  73. package/dist/client/assets/{twig-BRlCEyLn.js → twig-CBMvmisW.js} +2 -2
  74. package/dist/client/assets/{typescript-DC1qlCSF.js → typescript-CD8iaNuH.js} +2 -2
  75. package/dist/client/assets/{vue-Cta_ijHF.js → vue-BOpxstHd.js} +2 -2
  76. package/dist/client/assets/{vue-html-C6cjjUjR.js → vue-html-C7Ua1z7r.js} +2 -2
  77. package/dist/client/assets/{vue-vine-DMiSqDlT.js → vue-vine-BW2B7VA6.js} +2 -2
  78. package/dist/client/assets/{xml-DJpzo_Kg.js → xml-J7P8pGRv.js} +2 -2
  79. package/dist/client/assets/{xsl-DAqFroi4.js → xsl-B-mxb3Nx.js} +2 -2
  80. package/dist/client/assets/{yaml-Brab7tCG.js → yaml-IDU07r3p.js} +2 -2
  81. package/dist/client/index.html +2 -2
  82. package/package.json +1 -1
  83. package/dist/client/assets/arrow-right-BHEVGFLQ.js +0 -2
  84. package/dist/client/assets/textarea-C6kz4NLj.js +0 -46
package/dist/bin.mjs CHANGED
@@ -233,7 +233,7 @@ const make$72 = () => {
233
233
  const layer$68 = Layer.sync(NetService, make$72);
234
234
  //#endregion
235
235
  //#region package.json
236
- var version = "0.0.51";
236
+ var version = "0.0.53";
237
237
  //#endregion
238
238
  //#region src/config.ts
239
239
  /**
@@ -7734,6 +7734,47 @@ const AgentAssetDeleteInput = Schema$1.Struct({
7734
7734
  expectedVersion: Schema$1.optional(PositiveInt)
7735
7735
  });
7736
7736
  /**
7737
+ * Create an asset on this machine's disk, and only there.
7738
+ *
7739
+ * The hub is not involved: a new skill starts as an unmanaged local file, the
7740
+ * same as one written by hand, and becomes shared only through an explicit
7741
+ * publish. That keeps "nothing uploads by accident" true for assets p4code
7742
+ * itself creates, and it means creation works on a machine with no hub
7743
+ * configured at all.
7744
+ */
7745
+ const AgentAssetCreateLocalInput = Schema$1.Struct({
7746
+ kind: AgentAssetKind,
7747
+ name: AgentAssetName,
7748
+ files: AgentAssetFiles
7749
+ });
7750
+ /**
7751
+ * What a local create or remove did, or why it did nothing.
7752
+ *
7753
+ * An enum rather than an error, because every branch is an ordinary answer a
7754
+ * panel has to render: `exists` and `missing` are races with the filesystem,
7755
+ * `managed` is the guard that keeps a hub-synced asset from being deleted
7756
+ * locally only to be re-materialized by the next sync pass, `refused` is a
7757
+ * policy answer (a skill without `SKILL.md`, a memory name outside the
7758
+ * allowlist, an MCP registration that belongs to `mcp.save`), `no-root` is an
7759
+ * unconfigured provider, and `failed` is a disk that would not take the write.
7760
+ */
7761
+ const AgentAssetLocalMutationOutcome = Schema$1.Literals([
7762
+ "created",
7763
+ "removed",
7764
+ "exists",
7765
+ "missing",
7766
+ "managed",
7767
+ "refused",
7768
+ "no-root",
7769
+ "failed"
7770
+ ]);
7771
+ const AgentAssetLocalMutation = Schema$1.Struct({
7772
+ outcome: AgentAssetLocalMutationOutcome,
7773
+ /** Absolute path of the created asset, so a panel can hand it to an agent. */
7774
+ path: Schema$1.NullOr(Schema$1.String),
7775
+ detail: Schema$1.NullOr(Schema$1.String)
7776
+ });
7777
+ /**
7737
7778
  * Changing the hub link failed.
7738
7779
  *
7739
7780
  * Only the link operations can fail. Syncing cannot: an unreachable hub, a
@@ -8153,6 +8194,8 @@ const WS_METHODS = {
8153
8194
  assetsRead: "assets.read",
8154
8195
  assetsSave: "assets.save",
8155
8196
  assetsDelete: "assets.delete",
8197
+ assetsCreateLocal: "assets.createLocal",
8198
+ assetsRemoveLocal: "assets.removeLocal",
8156
8199
  mcpList: "mcp.list",
8157
8200
  mcpSave: "mcp.save",
8158
8201
  mcpRemove: "mcp.remove",
@@ -8351,6 +8394,31 @@ const WsAssetsDeleteRpc = Rpc.make(WS_METHODS.assetsDelete, {
8351
8394
  error: Schema$1.Union([EnvironmentAuthorizationError])
8352
8395
  });
8353
8396
  /**
8397
+ * Creating and removing assets on this machine only.
8398
+ *
8399
+ * These are the hub-free half of asset management: they write and delete the
8400
+ * local files directly, work on a server with no hub configured, and leave
8401
+ * sharing to the explicit publish. Removal refuses managed assets — deleting
8402
+ * only the local copy of a synced asset would just re-materialize on the next
8403
+ * pass, so the honest operations there are unpublish or `assets.delete`.
8404
+ */
8405
+ const WsAssetsCreateLocalRpc = Rpc.make(WS_METHODS.assetsCreateLocal, {
8406
+ payload: AgentAssetCreateLocalInput,
8407
+ success: Schema$1.Struct({
8408
+ result: AgentAssetLocalMutation,
8409
+ status: AgentSyncStatus
8410
+ }),
8411
+ error: Schema$1.Union([EnvironmentAuthorizationError])
8412
+ });
8413
+ const WsAssetsRemoveLocalRpc = Rpc.make(WS_METHODS.assetsRemoveLocal, {
8414
+ payload: AgentAssetRefInput,
8415
+ success: Schema$1.Struct({
8416
+ result: AgentAssetLocalMutation,
8417
+ status: AgentSyncStatus
8418
+ }),
8419
+ error: Schema$1.Union([EnvironmentAuthorizationError])
8420
+ });
8421
+ /**
8354
8422
  * Registering the user's own MCP servers.
8355
8423
  *
8356
8424
  * Every one of these answers with the whole list, for the same reason the sync
@@ -8750,7 +8818,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
8750
8818
  error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
8751
8819
  stream: true
8752
8820
  });
8753
- const WsRpcGroup = RpcGroup.make(WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
8821
+ const WsRpcGroup = RpcGroup.make(WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
8754
8822
  //#endregion
8755
8823
  //#region ../../packages/shared/src/oauthScope.ts
8756
8824
  const OAUTH_SCOPE_TOKEN = /^[\u0021\u0023-\u005b\u005d-\u007e]+$/u;
@@ -15593,6 +15661,48 @@ const writeSkillManifest = Effect.fn("skillManifest.write")(function* (manifestP
15593
15661
  });
15594
15662
  });
15595
15663
  //#endregion
15664
+ //#region src/sync/localAssetOps.ts
15665
+ const refuse = (outcome, detail) => ({
15666
+ _tag: "refuse",
15667
+ outcome,
15668
+ detail
15669
+ });
15670
+ const PROCEED = { _tag: "proceed" };
15671
+ /**
15672
+ * May this asset be created here?
15673
+ *
15674
+ * - MCP registrations have their own write path (`mcp.save`), which is where
15675
+ * the definition/secret split is enforced. Creating one as a bare file would
15676
+ * bypass the decode-before-write rule, so it is refused rather than routed.
15677
+ * - A skill without `SKILL.md` would be invisible to discovery: the picker
15678
+ * skips directories without one, so the create would look like it silently
15679
+ * failed. Refused with the reason instead.
15680
+ * - An existing local name is never overwritten. Create means create; editing
15681
+ * is a different operation with a different surface.
15682
+ */
15683
+ function decideCreateLocal(input) {
15684
+ if (input.asset.kind === "mcp") return refuse("refused", "MCP registrations are created through mcp.save, not as files.");
15685
+ if (input.asset.kind === "skill" && !input.asset.files.some((file) => file.path === "SKILL.md")) return refuse("refused", "A skill needs a SKILL.md at its root; without one, discovery skips the directory.");
15686
+ if (input.localNames.has(input.asset.name)) return refuse("exists", `An asset named '${input.asset.name}' already exists locally.`);
15687
+ return PROCEED;
15688
+ }
15689
+ /**
15690
+ * May this asset be removed from this machine?
15691
+ *
15692
+ * The managed refusal is the load-bearing one. A managed asset is a hub-synced
15693
+ * asset, and the sync plan's answer to "managed and missing locally" is to
15694
+ * re-materialize it - so a local-only delete would quietly undo itself on the
15695
+ * next pass. The honest operations for a managed asset are unpublish (keep it
15696
+ * here, stop sharing) or a hub delete (remove it everywhere), and the refusal
15697
+ * detail names both.
15698
+ */
15699
+ function decideRemoveLocal(input) {
15700
+ if (input.ref.kind === "mcp") return refuse("refused", "MCP registrations are removed through mcp.remove, not as files.");
15701
+ if (input.managedNames.has(input.ref.name)) return refuse("managed", `'${input.ref.name}' is synced with the hub; deleting it here would be undone by the next sync. Unpublish it to keep sharing off this machine, or delete it from the hub to remove it everywhere.`);
15702
+ if (!input.localNames.has(input.ref.name)) return refuse("missing", `No local asset named '${input.ref.name}' exists.`);
15703
+ return PROCEED;
15704
+ }
15705
+ //#endregion
15596
15706
  //#region src/sync/skillSyncPlan.ts
15597
15707
  /**
15598
15708
  * Deciding what a skill sync should do, given three views of the world.
@@ -16362,6 +16472,85 @@ const make$58 = Effect.gen(function* () {
16362
16472
  };
16363
16473
  return yield* run;
16364
16474
  }).pipe(Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("asset delete failed", { cause }).pipe(Effect.as(EMPTY_REPORT))));
16475
+ const FAILED_MUTATION = {
16476
+ outcome: "failed",
16477
+ path: null,
16478
+ detail: "The write failed; see the server log."
16479
+ };
16480
+ const createLocal = (input) => Effect.gen(function* () {
16481
+ const driver = driverFor(input.kind);
16482
+ if (driver === void 0) return {
16483
+ outcome: "refused",
16484
+ path: null,
16485
+ detail: `Unknown asset kind '${input.kind}'.`
16486
+ };
16487
+ const root = yield* driver.resolveRoot;
16488
+ if (root === null) return {
16489
+ outcome: "no-root",
16490
+ path: null,
16491
+ detail: "This asset kind has no home here - the provider is not configured."
16492
+ };
16493
+ const local = yield* driver.read(root);
16494
+ const decision = decideCreateLocal({
16495
+ asset: input,
16496
+ localNames: new Set(local.skills.map((skill) => skill.name))
16497
+ });
16498
+ if (decision._tag === "refuse") return {
16499
+ outcome: decision.outcome,
16500
+ path: null,
16501
+ detail: decision.detail
16502
+ };
16503
+ const written = yield* driver.write({
16504
+ root,
16505
+ name: input.name,
16506
+ files: input.files
16507
+ });
16508
+ if (written !== "written") return {
16509
+ outcome: written,
16510
+ path: null,
16511
+ detail: written === "refused" ? "This kind does not accept that name here." : "The disk would not take the write."
16512
+ };
16513
+ return {
16514
+ outcome: "created",
16515
+ path: path.join(root, input.name),
16516
+ detail: null
16517
+ };
16518
+ }).pipe(Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("asset create-local failed", { cause }).pipe(Effect.as(FAILED_MUTATION))));
16519
+ const removeLocal = (ref) => Effect.gen(function* () {
16520
+ const driver = driverFor(ref.kind);
16521
+ if (driver === void 0) return {
16522
+ outcome: "refused",
16523
+ path: null,
16524
+ detail: `Unknown asset kind '${ref.kind}'.`
16525
+ };
16526
+ const root = yield* driver.resolveRoot;
16527
+ if (root === null) return {
16528
+ outcome: "no-root",
16529
+ path: null,
16530
+ detail: "This asset kind has no home here - the provider is not configured."
16531
+ };
16532
+ const local = yield* driver.read(root);
16533
+ const manifest = yield* readSkillManifest(manifestPath);
16534
+ const decision = decideRemoveLocal({
16535
+ ref,
16536
+ localNames: new Set(local.skills.map((skill) => skill.name)),
16537
+ managedNames: new Set(entriesFor(manifest, root).map((entry) => entry.name))
16538
+ });
16539
+ if (decision._tag === "refuse") return {
16540
+ outcome: decision.outcome,
16541
+ path: null,
16542
+ detail: decision.detail
16543
+ };
16544
+ yield* driver.remove({
16545
+ root,
16546
+ name: ref.name
16547
+ });
16548
+ return {
16549
+ outcome: "removed",
16550
+ path: null,
16551
+ detail: null
16552
+ };
16553
+ }).pipe(Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("asset remove-local failed", { cause }).pipe(Effect.as(FAILED_MUTATION))));
16365
16554
  return {
16366
16555
  run,
16367
16556
  status: assetStatus,
@@ -16370,7 +16559,9 @@ const make$58 = Effect.gen(function* () {
16370
16559
  unpublish,
16371
16560
  read,
16372
16561
  save,
16373
- deleteFromHub
16562
+ deleteFromHub,
16563
+ createLocal,
16564
+ removeLocal
16374
16565
  };
16375
16566
  });
16376
16567
  const layer$53 = Layer.effect(AssetSync, make$58);
@@ -31137,11 +31328,16 @@ function dropStaleContextWindowActivities(activities) {
31137
31328
  const latestIndexByTurn = /* @__PURE__ */ new Map();
31138
31329
  for (let index = 0; index < activities.length; index += 1) if (isResolvableContextWindowActivity(activities[index])) latestIndexByTurn.set(activities[index].turnId, index);
31139
31330
  if (latestIndexByTurn.size === 0) return activities;
31140
- const newestIndex = Math.max(...latestIndexByTurn.values());
31331
+ const retainedIndexes = new Set(latestIndexByTurn.values());
31332
+ let breakdownIndex = null;
31333
+ for (const index of retainedIndexes) {
31334
+ if (asRecord$1(activities[index].payload)?.breakdown === void 0) continue;
31335
+ if (breakdownIndex === null || index > breakdownIndex) breakdownIndex = index;
31336
+ }
31141
31337
  return activities.flatMap((activity, index) => {
31142
31338
  if (!isResolvableContextWindowActivity(activity)) return [activity];
31143
31339
  if (latestIndexByTurn.get(activity.turnId) !== index) return [];
31144
- return [index === newestIndex ? activity : withoutContextWindowBreakdown$1(activity)];
31340
+ return [index === breakdownIndex ? activity : withoutContextWindowBreakdown$1(activity)];
31145
31341
  });
31146
31342
  }
31147
31343
  function projectThreadDetailSnapshot(snapshot) {
@@ -41123,6 +41319,8 @@ const RPC_REQUIRED_SCOPE = /* @__PURE__ */ new Map([
41123
41319
  [WS_METHODS.assetsRead, AuthOrchestrationReadScope],
41124
41320
  [WS_METHODS.assetsSave, AuthOrchestrationOperateScope],
41125
41321
  [WS_METHODS.assetsDelete, AuthOrchestrationOperateScope],
41322
+ [WS_METHODS.assetsCreateLocal, AuthOrchestrationOperateScope],
41323
+ [WS_METHODS.assetsRemoveLocal, AuthOrchestrationOperateScope],
41126
41324
  [WS_METHODS.mcpList, AuthOrchestrationReadScope],
41127
41325
  [WS_METHODS.mcpSave, AuthOrchestrationOperateScope],
41128
41326
  [WS_METHODS.mcpRemove, AuthOrchestrationOperateScope],
@@ -41807,6 +42005,18 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
41807
42005
  status: yield* assetSync.status
41808
42006
  };
41809
42007
  }), { "rpc.aggregate": "skills" }),
42008
+ [WS_METHODS.assetsCreateLocal]: (input) => observeRpcEffect$1(WS_METHODS.assetsCreateLocal, Effect.gen(function* () {
42009
+ return {
42010
+ result: yield* assetSync.createLocal(input),
42011
+ status: yield* assetSync.status
42012
+ };
42013
+ }), { "rpc.aggregate": "skills" }),
42014
+ [WS_METHODS.assetsRemoveLocal]: (input) => observeRpcEffect$1(WS_METHODS.assetsRemoveLocal, Effect.gen(function* () {
42015
+ return {
42016
+ result: yield* assetSync.removeLocal(input),
42017
+ status: yield* assetSync.status
42018
+ };
42019
+ }), { "rpc.aggregate": "skills" }),
41810
42020
  [WS_METHODS.mcpList]: (_input) => observeRpcEffect$1(WS_METHODS.mcpList, mcpRegistry.list.pipe(Effect.map((servers) => ({ servers }))), { "rpc.aggregate": "mcp" }),
41811
42021
  [WS_METHODS.mcpSave]: ({ registration }) => observeRpcEffect$1(WS_METHODS.mcpSave, mcpRegistry.save(registration).pipe(Effect.andThen(mcpRegistry.list.pipe(Effect.map((servers) => ({ servers }))))), { "rpc.aggregate": "mcp" }),
41812
42022
  [WS_METHODS.mcpRemove]: ({ name }) => observeRpcEffect$1(WS_METHODS.mcpRemove, mcpRegistry.remove(name).pipe(Effect.andThen(mcpRegistry.list.pipe(Effect.map((servers) => ({ servers }))))), { "rpc.aggregate": "mcp" }),
@@ -46444,15 +46654,28 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
46444
46654
  });
46445
46655
  });
46446
46656
  const queryCurrentContextUsage = Effect.fn("queryCurrentContextUsage")(function* (context, totalProcessedTokens) {
46447
- if (!context.query.getContextUsage) return;
46657
+ if (!context.query.getContextUsage) {
46658
+ yield* Effect.logWarning("claude.context-usage.unsupported", { threadId: context.session.threadId });
46659
+ return;
46660
+ }
46448
46661
  const usage = yield* Effect.promise(async () => {
46449
46662
  try {
46450
46663
  return await context.query.getContextUsage?.();
46451
- } catch {
46452
- return;
46664
+ } catch (cause) {
46665
+ return { failure: cause instanceof Error ? cause.message : String(cause) };
46453
46666
  }
46454
46667
  });
46455
- if (!usage) return;
46668
+ if (usage !== void 0 && "failure" in usage) {
46669
+ yield* Effect.logWarning("claude.context-usage.failed", {
46670
+ threadId: context.session.threadId,
46671
+ detail: usage.failure
46672
+ });
46673
+ return;
46674
+ }
46675
+ if (!usage) {
46676
+ yield* Effect.logWarning("claude.context-usage.empty", { threadId: context.session.threadId });
46677
+ return;
46678
+ }
46456
46679
  context.lastKnownContextWindow = usage.maxTokens;
46457
46680
  return normalizeClaudeContextUsageApiSnapshot(usage, totalProcessedTokens);
46458
46681
  });
@@ -46625,6 +46848,80 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
46625
46848
  };
46626
46849
  yield* updateResumeCursor(context);
46627
46850
  });
46851
+ const applyTurnSessionOptions = Effect.fn("applyTurnSessionOptions")(function* (context, modelSelection, interactionMode) {
46852
+ if (modelSelection?.model) {
46853
+ const apiModelId = resolveClaudeApiModelId(modelSelection);
46854
+ if (context.currentApiModelId !== apiModelId) {
46855
+ yield* Effect.tryPromise({
46856
+ try: () => context.query.setModel(apiModelId),
46857
+ catch: (cause) => toRequestError$1(context.session.threadId, "turn/setModel", cause)
46858
+ });
46859
+ context.currentApiModelId = apiModelId;
46860
+ }
46861
+ context.session = {
46862
+ ...context.session,
46863
+ model: modelSelection.model
46864
+ };
46865
+ }
46866
+ if (interactionMode === "plan") yield* Effect.tryPromise({
46867
+ try: () => context.query.setPermissionMode("plan"),
46868
+ catch: (cause) => toRequestError$1(context.session.threadId, "turn/setPermissionMode", cause)
46869
+ });
46870
+ else if (interactionMode === "default") yield* Effect.tryPromise({
46871
+ try: () => context.query.setPermissionMode(context.basePermissionMode ?? "default"),
46872
+ catch: (cause) => toRequestError$1(context.session.threadId, "turn/setPermissionMode", cause)
46873
+ });
46874
+ });
46875
+ const beginTurn = Effect.fn("beginTurn")(function* (context, turnId, model) {
46876
+ const turnState = {
46877
+ turnId,
46878
+ startedAt: yield* nowIso,
46879
+ items: [],
46880
+ assistantTextBlocks: /* @__PURE__ */ new Map(),
46881
+ assistantTextBlockOrder: [],
46882
+ capturedProposedPlanKeys: /* @__PURE__ */ new Set(),
46883
+ nextSyntheticAssistantBlockIndex: -1
46884
+ };
46885
+ const updatedAt = yield* nowIso;
46886
+ context.turnState = turnState;
46887
+ context.session = {
46888
+ ...context.session,
46889
+ status: "running",
46890
+ activeTurnId: turnId,
46891
+ updatedAt
46892
+ };
46893
+ const turnStartedStamp = yield* makeEventStamp();
46894
+ yield* offerRuntimeEvent({
46895
+ type: "turn.started",
46896
+ eventId: turnStartedStamp.eventId,
46897
+ provider: PROVIDER$5,
46898
+ createdAt: turnStartedStamp.createdAt,
46899
+ threadId: context.session.threadId,
46900
+ turnId,
46901
+ payload: model ? { model } : {},
46902
+ providerRefs: {}
46903
+ });
46904
+ });
46905
+ /**
46906
+ * Start the next queued sendTurn, if the session has gone idle.
46907
+ *
46908
+ * Runs after every turn completion. A message sent during a running turn
46909
+ * waits here for the agent loop to end; pressing Stop ends the loop early,
46910
+ * and the same drain is what makes Stop mean "and run my message now".
46911
+ * One request per drain, so each queued message gets its own turn boundary -
46912
+ * the next one starts when this turn's result arrives.
46913
+ */
46914
+ const drainPendingTurns = Effect.fn("drainPendingTurns")(function* (context) {
46915
+ if (context.stopped || context.turnState !== void 0) return;
46916
+ const pending = context.pendingTurnRequests.shift();
46917
+ if (pending === void 0) return;
46918
+ yield* applyTurnSessionOptions(context, pending.modelSelection, pending.interactionMode).pipe(Effect.catchCause((cause) => Effect.logWarning("claude pending turn session options failed", { cause })));
46919
+ yield* beginTurn(context, pending.turnId, pending.modelSelection?.model);
46920
+ yield* Queue.offer(context.promptQueue, {
46921
+ type: "message",
46922
+ message: pending.message
46923
+ }).pipe(Effect.ignore);
46924
+ });
46628
46925
  const handleStreamEvent = Effect.fn("handleStreamEvent")(function* (context, message) {
46629
46926
  if (message.type !== "stream_event") return;
46630
46927
  const { event } = message;
@@ -46960,6 +47257,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
46960
47257
  const errorMessage = message.subtype === "success" ? void 0 : message.errors[0];
46961
47258
  if (status === "failed") yield* emitRuntimeError(context, errorMessage ?? "Claude turn failed.");
46962
47259
  yield* completeTurn(context, status, errorMessage, message);
47260
+ yield* drainPendingTurns(context);
46963
47261
  });
46964
47262
  const handleSystemMessage = Effect.fn("handleSystemMessage")(function* (context, message) {
46965
47263
  if (message.type !== "system") return;
@@ -47700,6 +47998,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
47700
47998
  inFlightTools,
47701
47999
  claudeTasks,
47702
48000
  turnState: void 0,
48001
+ pendingTurnRequests: [],
47703
48002
  lastKnownContextWindow: initialContextWindow,
47704
48003
  lastKnownTokenUsage: void 0,
47705
48004
  lastKnownTotalProcessedTokens: void 0,
@@ -47760,66 +48059,29 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
47760
48059
  const sendTurn = Effect.fn("sendTurn")(function* (input) {
47761
48060
  const context = yield* requireSession(input.threadId);
47762
48061
  const modelSelection = input.modelSelection !== void 0 && input.modelSelection.instanceId === boundInstanceId ? input.modelSelection : void 0;
47763
- const steeringTurnState = context.turnState && context.turnState.synthetic !== true ? context.turnState : null;
47764
- if (context.turnState && steeringTurnState === null) yield* completeTurn(context, "completed");
47765
- if (modelSelection?.model) {
47766
- const apiModelId = resolveClaudeApiModelId(modelSelection);
47767
- if (context.currentApiModelId !== apiModelId) {
47768
- yield* Effect.tryPromise({
47769
- try: () => context.query.setModel(apiModelId),
47770
- catch: (cause) => toRequestError$1(input.threadId, "turn/setModel", cause)
47771
- });
47772
- context.currentApiModelId = apiModelId;
47773
- }
47774
- context.session = {
47775
- ...context.session,
47776
- model: modelSelection.model
47777
- };
47778
- }
47779
- if (input.interactionMode === "plan") yield* Effect.tryPromise({
47780
- try: () => context.query.setPermissionMode("plan"),
47781
- catch: (cause) => toRequestError$1(input.threadId, "turn/setPermissionMode", cause)
47782
- });
47783
- else if (input.interactionMode === "default") yield* Effect.tryPromise({
47784
- try: () => context.query.setPermissionMode(context.basePermissionMode ?? "default"),
47785
- catch: (cause) => toRequestError$1(input.threadId, "turn/setPermissionMode", cause)
47786
- });
47787
- const turnId = steeringTurnState?.turnId ?? TurnId.make(yield* randomUUIDv4);
47788
- if (steeringTurnState === null) {
47789
- const turnState = {
47790
- turnId,
47791
- startedAt: yield* nowIso,
47792
- items: [],
47793
- assistantTextBlocks: /* @__PURE__ */ new Map(),
47794
- assistantTextBlockOrder: [],
47795
- capturedProposedPlanKeys: /* @__PURE__ */ new Set(),
47796
- nextSyntheticAssistantBlockIndex: -1
47797
- };
47798
- const updatedAt = yield* nowIso;
47799
- context.turnState = turnState;
47800
- context.session = {
47801
- ...context.session,
47802
- status: "running",
47803
- activeTurnId: turnId,
47804
- updatedAt
47805
- };
47806
- const turnStartedStamp = yield* makeEventStamp();
47807
- yield* offerRuntimeEvent({
47808
- type: "turn.started",
47809
- eventId: turnStartedStamp.eventId,
47810
- provider: PROVIDER$5,
47811
- createdAt: turnStartedStamp.createdAt,
47812
- threadId: context.session.threadId,
47813
- turnId,
47814
- payload: modelSelection?.model ? { model: modelSelection.model } : {},
47815
- providerRefs: {}
47816
- });
47817
- }
48062
+ if (context.turnState?.synthetic === true) yield* completeTurn(context, "completed");
47818
48063
  const message = yield* buildUserMessageEffect(input, {
47819
48064
  fileSystem,
47820
48065
  attachmentsDir: serverConfig.attachmentsDir,
47821
48066
  boundInstanceId
47822
48067
  });
48068
+ if (context.turnState) {
48069
+ const turnId = TurnId.make(yield* randomUUIDv4);
48070
+ context.pendingTurnRequests.push({
48071
+ turnId,
48072
+ message,
48073
+ modelSelection,
48074
+ interactionMode: input.interactionMode
48075
+ });
48076
+ return {
48077
+ threadId: context.session.threadId,
48078
+ turnId,
48079
+ ...context.session.resumeCursor !== void 0 ? { resumeCursor: context.session.resumeCursor } : {}
48080
+ };
48081
+ }
48082
+ yield* applyTurnSessionOptions(context, modelSelection, input.interactionMode);
48083
+ const turnId = TurnId.make(yield* randomUUIDv4);
48084
+ yield* beginTurn(context, turnId, modelSelection?.model);
47823
48085
  yield* Queue.offer(context.promptQueue, {
47824
48086
  type: "message",
47825
48087
  message
@@ -82387,6 +82649,15 @@ const make$1 = Effect.gen(function* () {
82387
82649
  }).pipe(Effect.catch(() => Effect.void));
82388
82650
  return;
82389
82651
  }
82652
+ if ((yield* providerService.listSessions()).find((session) => session.threadId !== event.payload.threadId && session.cwd === sessionRuntime.value.cwd)) {
82653
+ yield* appendRevertFailureActivity({
82654
+ threadId: event.payload.threadId,
82655
+ turnCount: event.payload.turnCount,
82656
+ detail: "Another active thread is working in the same directory; reverting would overwrite or delete its uncommitted work. Settle or stop that thread first.",
82657
+ createdAt: now
82658
+ }).pipe(Effect.catch(() => Effect.void));
82659
+ return;
82660
+ }
82390
82661
  const currentTurnCount = thread.checkpoints.reduce((maxTurnCount, checkpoint) => Math.max(maxTurnCount, checkpoint.checkpointTurnCount), 0);
82391
82662
  if (event.payload.turnCount > currentTurnCount) {
82392
82663
  yield* appendRevertFailureActivity({