@zihanw/pi-forge 0.4.0-beta.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/CHANGELOG.md +45 -1
  2. package/PUBLIC_API.md +3 -26
  3. package/README.md +90 -601
  4. package/README.zh-CN.md +86 -585
  5. package/SUBAGENT_ADAPTER_CONTRACT.md +3 -197
  6. package/dist/agent-profile.d.ts +24 -1
  7. package/dist/agent-profile.d.ts.map +1 -1
  8. package/dist/agent-profile.js +146 -36
  9. package/dist/agent-profile.js.map +1 -1
  10. package/dist/catalog.d.ts +27 -0
  11. package/dist/catalog.d.ts.map +1 -0
  12. package/dist/catalog.js +59 -0
  13. package/dist/catalog.js.map +1 -0
  14. package/dist/forge-config.d.ts +106 -0
  15. package/dist/forge-config.d.ts.map +1 -1
  16. package/dist/forge-config.js +305 -18
  17. package/dist/forge-config.js.map +1 -1
  18. package/dist/index.d.ts +5 -5
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +55 -8
  21. package/dist/index.js.map +1 -1
  22. package/dist/lifecycle.d.ts +2 -1
  23. package/dist/lifecycle.d.ts.map +1 -1
  24. package/dist/lifecycle.js +7 -4
  25. package/dist/lifecycle.js.map +1 -1
  26. package/dist/loader.d.ts +17 -1
  27. package/dist/loader.d.ts.map +1 -1
  28. package/dist/loader.js +84 -25
  29. package/dist/loader.js.map +1 -1
  30. package/dist/preset-command.d.ts +1 -1
  31. package/dist/preset-command.d.ts.map +1 -1
  32. package/dist/preset-command.js +38 -10
  33. package/dist/preset-command.js.map +1 -1
  34. package/dist/profile-command.d.ts.map +1 -1
  35. package/dist/profile-command.js +46 -21
  36. package/dist/profile-command.js.map +1 -1
  37. package/dist/profile-service.d.ts +6 -3
  38. package/dist/profile-service.d.ts.map +1 -1
  39. package/dist/profile-service.js +55 -13
  40. package/dist/profile-service.js.map +1 -1
  41. package/dist/resource-identity.d.ts +33 -0
  42. package/dist/resource-identity.d.ts.map +1 -0
  43. package/dist/resource-identity.js +56 -0
  44. package/dist/resource-identity.js.map +1 -0
  45. package/dist/runtime/profile-runtime.d.ts.map +1 -1
  46. package/dist/runtime/profile-runtime.js +7 -2
  47. package/dist/runtime/profile-runtime.js.map +1 -1
  48. package/dist/runtime/prompt-stack-runtime.d.ts +1 -1
  49. package/dist/runtime/prompt-stack-runtime.d.ts.map +1 -1
  50. package/dist/runtime/prompt-stack-runtime.js +22 -12
  51. package/dist/runtime/prompt-stack-runtime.js.map +1 -1
  52. package/dist/runtime/subagent-runtime.d.ts +23 -8
  53. package/dist/runtime/subagent-runtime.d.ts.map +1 -1
  54. package/dist/runtime/subagent-runtime.js +296 -67
  55. package/dist/runtime/subagent-runtime.js.map +1 -1
  56. package/dist/storage.d.ts +11 -0
  57. package/dist/storage.d.ts.map +1 -1
  58. package/dist/storage.js +51 -2
  59. package/dist/storage.js.map +1 -1
  60. package/dist/subagent/canonical.d.ts +19 -7
  61. package/dist/subagent/canonical.d.ts.map +1 -1
  62. package/dist/subagent/canonical.js +19 -47
  63. package/dist/subagent/canonical.js.map +1 -1
  64. package/dist/subagent/contract.d.ts +1 -2
  65. package/dist/subagent/contract.d.ts.map +1 -1
  66. package/dist/subagent/contract.js +1 -2
  67. package/dist/subagent/contract.js.map +1 -1
  68. package/dist/subagent/index.d.ts +4 -3
  69. package/dist/subagent/index.d.ts.map +1 -1
  70. package/dist/subagent/index.js +4 -3
  71. package/dist/subagent/index.js.map +1 -1
  72. package/dist/subagent/plan.d.ts +5 -1
  73. package/dist/subagent/plan.d.ts.map +1 -1
  74. package/dist/subagent/plan.js +29 -32
  75. package/dist/subagent/plan.js.map +1 -1
  76. package/dist/subagent/request.d.ts.map +1 -1
  77. package/dist/subagent/request.js +36 -4
  78. package/dist/subagent/request.js.map +1 -1
  79. package/dist/subagent/types.d.ts +66 -178
  80. package/dist/subagent/types.d.ts.map +1 -1
  81. package/dist/subagent/types.js +1 -1
  82. package/dist/subagent/types.js.map +1 -1
  83. package/dist/subagent/validation.d.ts +14 -14
  84. package/dist/subagent/validation.d.ts.map +1 -1
  85. package/dist/subagent/validation.js +52 -238
  86. package/dist/subagent/validation.js.map +1 -1
  87. package/dist/subagent-command.d.ts +2 -1
  88. package/dist/subagent-command.d.ts.map +1 -1
  89. package/dist/subagent-command.js +115 -19
  90. package/dist/subagent-command.js.map +1 -1
  91. package/dist/subagent-host.d.ts.map +1 -1
  92. package/dist/subagent-host.js +43 -16
  93. package/dist/subagent-host.js.map +1 -1
  94. package/dist/subagent-profile-tool.d.ts +25 -2
  95. package/dist/subagent-profile-tool.d.ts.map +1 -1
  96. package/dist/subagent-profile-tool.js +41 -9
  97. package/dist/subagent-profile-tool.js.map +1 -1
  98. package/dist/subagent-tool.d.ts +31 -4
  99. package/dist/subagent-tool.d.ts.map +1 -1
  100. package/dist/subagent-tool.js +311 -137
  101. package/dist/subagent-tool.js.map +1 -1
  102. package/dist/types.d.ts +3 -0
  103. package/dist/types.d.ts.map +1 -1
  104. package/dist/types.js.map +1 -1
  105. package/dist/web-editor/client-script.generated.d.ts +1 -1
  106. package/dist/web-editor/client-script.generated.d.ts.map +1 -1
  107. package/dist/web-editor/client-script.generated.js +1 -1
  108. package/dist/web-editor/client-script.generated.js.map +1 -1
  109. package/dist/web-editor/client-styles.d.ts +2 -0
  110. package/dist/web-editor/client-styles.d.ts.map +1 -0
  111. package/dist/web-editor/client-styles.generated.d.ts +2 -0
  112. package/dist/web-editor/client-styles.generated.d.ts.map +1 -0
  113. package/dist/web-editor/client-styles.generated.js +3 -0
  114. package/dist/web-editor/client-styles.generated.js.map +1 -0
  115. package/dist/web-editor/client-styles.js +2 -0
  116. package/dist/web-editor/client-styles.js.map +1 -0
  117. package/dist/web-editor/page.d.ts +2 -0
  118. package/dist/web-editor/page.d.ts.map +1 -1
  119. package/dist/web-editor/page.js +11 -73
  120. package/dist/web-editor/page.js.map +1 -1
  121. package/dist/web-editor/server.d.ts.map +1 -1
  122. package/dist/web-editor/server.js +176 -1
  123. package/dist/web-editor/server.js.map +1 -1
  124. package/dist/web-editor/styles.d.ts.map +1 -1
  125. package/dist/web-editor/styles.js +60 -3
  126. package/dist/web-editor/styles.js.map +1 -1
  127. package/dist/web-editor/types.d.ts +87 -0
  128. package/dist/web-editor/types.d.ts.map +1 -1
  129. package/dist/web-host.d.ts +13 -2
  130. package/dist/web-host.d.ts.map +1 -1
  131. package/dist/web-host.js +371 -27
  132. package/dist/web-host.js.map +1 -1
  133. package/docs/README.md +41 -0
  134. package/docs/concepts/agent-profiles.md +60 -0
  135. package/docs/concepts/prompt-stacks.md +95 -0
  136. package/docs/design/README.md +17 -0
  137. package/docs/design/roadmap-0.4-archive.md +216 -0
  138. package/docs/design/subagents/design-review.md +220 -0
  139. package/docs/design/subagents/interface-design.md +274 -0
  140. package/docs/design/subagents/sdk-spike-findings.md +117 -0
  141. package/docs/development/complexity-review.md +86 -0
  142. package/docs/development/release.md +31 -0
  143. package/docs/development/roadmap.md +36 -0
  144. package/docs/development/scoped-global-profiles-stacks.md +325 -0
  145. package/docs/development/setup.md +75 -0
  146. package/docs/getting-started.md +95 -0
  147. package/docs/guides/custom-macros-and-slots.md +68 -0
  148. package/docs/guides/debugging.md +39 -0
  149. package/docs/guides/delegation.md +107 -0
  150. package/docs/guides/sillytavern-import.md +47 -0
  151. package/docs/guides/use-cases.md +65 -0
  152. package/docs/guides/web-editor.md +75 -0
  153. package/docs/reference/commands.md +60 -0
  154. package/docs/reference/configuration.md +66 -0
  155. package/docs/reference/features.md +280 -0
  156. package/docs/reference/macros-and-slots.md +82 -0
  157. package/docs/reference/public-api.md +28 -0
  158. package/docs/reference/stack-schema.md +167 -0
  159. package/docs/reference/subagent-adapter.md +204 -0
  160. package/docs/zh-CN/README.md +37 -0
  161. package/docs/zh-CN/concepts/agent-profiles.md +46 -0
  162. package/docs/zh-CN/concepts/prompt-stacks.md +42 -0
  163. package/docs/zh-CN/getting-started.md +81 -0
  164. package/docs/zh-CN/guides/delegation.md +66 -0
  165. package/docs/zh-CN/guides/web-editor.md +45 -0
  166. package/docs/zh-CN/reference/commands.md +60 -0
  167. package/package.json +29 -15
  168. package/dist/subagent/backend-registry.d.ts +0 -75
  169. package/dist/subagent/backend-registry.d.ts.map +0 -1
  170. package/dist/subagent/backend-registry.js +0 -463
  171. package/dist/subagent/backend-registry.js.map +0 -1
  172. package/dist/subagent/diagnostics.d.ts +0 -3
  173. package/dist/subagent/diagnostics.d.ts.map +0 -1
  174. package/dist/subagent/diagnostics.js +0 -5
  175. package/dist/subagent/diagnostics.js.map +0 -1
  176. package/dist/subagent/pi-model-runtime.d.ts +0 -8
  177. package/dist/subagent/pi-model-runtime.d.ts.map +0 -1
  178. package/dist/subagent/pi-model-runtime.js +0 -22
  179. package/dist/subagent/pi-model-runtime.js.map +0 -1
  180. package/dist/subagent/pi-sdk-backend.d.ts +0 -23
  181. package/dist/subagent/pi-sdk-backend.d.ts.map +0 -1
  182. package/dist/subagent/pi-sdk-backend.js +0 -383
  183. package/dist/subagent/pi-sdk-backend.js.map +0 -1
  184. package/dist/subagent/pi-subprocess-backend.d.ts +0 -72
  185. package/dist/subagent/pi-subprocess-backend.d.ts.map +0 -1
  186. package/dist/subagent/pi-subprocess-backend.js +0 -756
  187. package/dist/subagent/pi-subprocess-backend.js.map +0 -1
  188. package/dist/subagent/subprocess-bridge.d.ts +0 -21
  189. package/dist/subagent/subprocess-bridge.d.ts.map +0 -1
  190. package/dist/subagent/subprocess-bridge.js +0 -87
  191. package/dist/subagent/subprocess-bridge.js.map +0 -1
  192. package/dist/subagent/subprocess-report.d.ts +0 -4
  193. package/dist/subagent/subprocess-report.d.ts.map +0 -1
  194. package/dist/subagent/subprocess-report.js +0 -55
  195. package/dist/subagent/subprocess-report.js.map +0 -1
  196. package/dist/subagent-contract.d.ts +0 -8
  197. package/dist/subagent-contract.d.ts.map +0 -1
  198. package/dist/subagent-contract.js +0 -8
  199. package/dist/subagent-contract.js.map +0 -1
  200. package/dist/web-editor/client/api.d.ts +0 -9
  201. package/dist/web-editor/client/api.d.ts.map +0 -1
  202. package/dist/web-editor/client/api.js +0 -26
  203. package/dist/web-editor/client/api.js.map +0 -1
  204. package/dist/web-editor/client/dom.d.ts +0 -13
  205. package/dist/web-editor/client/dom.d.ts.map +0 -1
  206. package/dist/web-editor/client/dom.js +0 -30
  207. package/dist/web-editor/client/dom.js.map +0 -1
  208. package/dist/web-editor/client/inspector.d.ts +0 -22
  209. package/dist/web-editor/client/inspector.d.ts.map +0 -1
  210. package/dist/web-editor/client/inspector.js +0 -226
  211. package/dist/web-editor/client/inspector.js.map +0 -1
  212. package/dist/web-editor/client/main.d.ts +0 -2
  213. package/dist/web-editor/client/main.d.ts.map +0 -1
  214. package/dist/web-editor/client/main.js +0 -1468
  215. package/dist/web-editor/client/main.js.map +0 -1
  216. package/dist/web-editor/client/policy-editor.d.ts +0 -16
  217. package/dist/web-editor/client/policy-editor.d.ts.map +0 -1
  218. package/dist/web-editor/client/policy-editor.js +0 -330
  219. package/dist/web-editor/client/policy-editor.js.map +0 -1
  220. package/dist/web-editor/client/regex-editor.d.ts +0 -19
  221. package/dist/web-editor/client/regex-editor.d.ts.map +0 -1
  222. package/dist/web-editor/client/regex-editor.js +0 -281
  223. package/dist/web-editor/client/regex-editor.js.map +0 -1
  224. package/dist/web-editor/client/types.d.ts +0 -60
  225. package/dist/web-editor/client/types.d.ts.map +0 -1
  226. package/dist/web-editor/client/types.js +0 -2
  227. package/dist/web-editor/client/types.js.map +0 -1
@@ -1,6 +1,10 @@
1
+ import type { AgentProfile, AgentProfileDiagnostic } from "../agent-profile.ts";
2
+ import type { AgentProfilePreview, AgentProfileRuntimeStatus } from "../profile-service.ts";
1
3
  import type { PromptStack, PromptStackDiagnostic } from "../types.ts";
2
4
  export interface WebEditorStackSummary {
3
5
  id: string;
6
+ selector: string;
7
+ scope: "global" | "project";
4
8
  name?: string;
5
9
  filePath: string;
6
10
  active: boolean;
@@ -14,6 +18,14 @@ export interface WebEditorStackSummary {
14
18
  export interface WebEditorHost {
15
19
  cwd: string;
16
20
  listStacks(): WebEditorStackSummary[];
21
+ listProfiles(): WebEditorProfileCollection;
22
+ reloadProfiles(): Promise<WebEditorOperationResult<WebEditorProfileCollection>>;
23
+ validateProfile(profile: AgentProfile, existingId?: string, scope?: "global" | "project"): WebEditorProfileValidation;
24
+ createProfile(profile: AgentProfile, scope?: "global" | "project"): Promise<WebEditorOperationResult<WebEditorProfileMutation>>;
25
+ saveProfile(id: string, profile: AgentProfile): Promise<WebEditorOperationResult<WebEditorProfileMutation>>;
26
+ applyProfile(id: string): Promise<WebEditorOperationResult<WebEditorProfileMutation>>;
27
+ deleteProfile(id: string): Promise<WebEditorOperationResult<WebEditorProfileMutation>>;
28
+ updateSubagentPolicy(id: string, update: WebEditorSubagentPolicyUpdate): Promise<WebEditorOperationResult<WebEditorProfileMutation>>;
17
29
  listResources(): WebEditorPolicyResources;
18
30
  getStack(id: string): {
19
31
  stack: PromptStack;
@@ -54,6 +66,79 @@ export interface WebEditorHost {
54
66
  stacks: WebEditorStackSummary[];
55
67
  }>>;
56
68
  }
69
+ export interface WebEditorProfileEntry {
70
+ profile: AgentProfile;
71
+ filePath: string;
72
+ selector: string;
73
+ scope: "global" | "project";
74
+ preview: AgentProfilePreview;
75
+ errors: number;
76
+ warnings: number;
77
+ lastApplied: boolean;
78
+ subagent: WebEditorSubagentProfilePolicy;
79
+ }
80
+ export interface WebEditorSubagentBackendOption {
81
+ id: string;
82
+ version: string;
83
+ }
84
+ export interface WebEditorSubagentProfilePolicy {
85
+ enabled: boolean;
86
+ enabledSource: "project-profile" | "global-profile" | "built-in";
87
+ backend: string;
88
+ backendSource: string;
89
+ backendRegistered: boolean;
90
+ timeoutMs: number;
91
+ timeoutSource: string;
92
+ configured: {
93
+ enabled?: boolean;
94
+ backend?: string;
95
+ timeoutMs?: number;
96
+ };
97
+ }
98
+ export interface WebEditorSubagentSummary {
99
+ defaultBackend: string;
100
+ defaultBackendSource: string;
101
+ timeoutMs: number;
102
+ timeoutSource: string;
103
+ allowAgentInvocationWithoutApproval: boolean;
104
+ backends: WebEditorSubagentBackendOption[];
105
+ warnings: string[];
106
+ }
107
+ export interface WebEditorSubagentPolicyUpdate {
108
+ enabled?: boolean;
109
+ backend?: string | null;
110
+ timeoutMs?: number | null;
111
+ }
112
+ export interface WebEditorProfileCollection {
113
+ trusted: boolean;
114
+ profileDirectory: string;
115
+ profiles: WebEditorProfileEntry[];
116
+ status: AgentProfileRuntimeStatus;
117
+ models: WebEditorProfileModelOption[];
118
+ promptStacks: Array<{
119
+ id: string;
120
+ name?: string;
121
+ selector: string;
122
+ scope: "global" | "project";
123
+ }>;
124
+ subagents: WebEditorSubagentSummary;
125
+ }
126
+ export interface WebEditorProfileModelOption {
127
+ provider: string;
128
+ id: string;
129
+ name?: string;
130
+ available: boolean;
131
+ }
132
+ export interface WebEditorProfileValidation {
133
+ preview: AgentProfilePreview;
134
+ diagnostics: AgentProfileDiagnostic[];
135
+ errors: number;
136
+ warnings: number;
137
+ }
138
+ export interface WebEditorProfileMutation {
139
+ collection: WebEditorProfileCollection;
140
+ selectedPath: string;
141
+ }
57
142
  export interface WebEditorPreviewSection {
58
143
  id: string;
59
144
  title: string;
@@ -94,6 +179,8 @@ export type WebEditorPayloadSnapshot = {
94
179
  export interface WebEditorCreateStackOptions {
95
180
  activate?: boolean;
96
181
  overwrite?: boolean;
182
+ /** Explicit target scope. Defaults to project for unqualified web create routes. */
183
+ scope?: "global" | "project";
97
184
  }
98
185
  export interface WebEditorPolicyResource {
99
186
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/web-editor/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/web-editor/types.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,8 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import type { LoadedPromptStack, PromptStackDiagnostic } from "./types.ts";
3
- import type { WebEditorHost, WebEditorOperationResult, WebEditorPayloadSnapshot, WebEditorPolicyResources, WebEditorPreview, WebEditorStackSummary } from "./web-editor/index.ts";
2
+ import { type AgentProfileProvenance, type LoadedAgentProfile, type ResolvedAgentProfile } from "./agent-profile.ts";
3
+ import { type AgentProfileApplicationResult, type AgentProfileCurrentRuntime } from "./profile-service.ts";
4
+ import type { LoadedPromptStack, PromptStack, PromptStackDiagnostic } from "./types.ts";
5
+ import type { WebEditorHost, WebEditorOperationResult, WebEditorPayloadSnapshot, WebEditorPolicyResources, WebEditorPreview, WebEditorStackSummary, WebEditorSubagentBackendOption } from "./web-editor/index.ts";
4
6
  export interface WebHostRuntime {
5
7
  getStacks(): LoadedPromptStack[];
6
8
  getActive(): LoadedPromptStack | undefined;
@@ -14,6 +16,15 @@ export interface WebHostRuntime {
14
16
  diagnostics: PromptStackDiagnostic[];
15
17
  };
16
18
  getPolicyResources(): WebEditorPolicyResources;
19
+ getProfiles(): LoadedAgentProfile[];
20
+ getLastAppliedProfile(): AgentProfileProvenance | undefined;
21
+ getCurrentProfileRuntime(): AgentProfileCurrentRuntime;
22
+ getSubagentBackends(): WebEditorSubagentBackendOption[];
23
+ resolveProfile(target: LoadedAgentProfile): ResolvedAgentProfile;
24
+ previewToolNames(stack: PromptStack | undefined): string[];
25
+ reloadProfiles(): void | Promise<void>;
26
+ isIdle(): boolean;
27
+ applyProfile(target: ResolvedAgentProfile): Promise<AgentProfileApplicationResult>;
17
28
  getPayload(): WebEditorOperationResult<WebEditorPayloadSnapshot>;
18
29
  armPayload(savePath?: string): WebEditorOperationResult<WebEditorPayloadSnapshot>;
19
30
  clearPayload(): WebEditorOperationResult<WebEditorPayloadSnapshot>;
@@ -1 +1 @@
1
- {"version":3,"file":"web-host.d.ts","sourceRoot":"","sources":["../src/web-host.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAMxE,OAAO,KAAK,EAAE,iBAAiB,EAAe,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxF,OAAO,KAAK,EAEX,aAAa,EACb,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,EACxB,gBAAgB,EAChB,qBAAqB,EACrB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,cAAc;IAC9B,SAAS,IAAI,iBAAiB,EAAE,CAAC;IACjC,SAAS,IAAI,iBAAiB,GAAG,SAAS,CAAC;IAC3C,WAAW,IAAI,MAAM,GAAG,SAAS,CAAC;IAClC,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAAC;IAC1C,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IAC3C,YAAY,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG;QACxC,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,gBAAgB,CAAC;QAC1B,WAAW,EAAE,qBAAqB,EAAE,CAAC;KACrC,CAAC;IACF,kBAAkB,IAAI,wBAAwB,CAAC;IAC/C,UAAU,IAAI,wBAAwB,CAAC,wBAAwB,CAAC,CAAC;IACjE,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,wBAAwB,CAAC,wBAAwB,CAAC,CAAC;IAClF,YAAY,IAAI,wBAAwB,CAAC,wBAAwB,CAAC,CAAC;CACnE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,cAAc,GAAG,aAAa,CAoCjG;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,GAAG,SAAS,GAAG,qBAAqB,CAepH;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,EAAE,EAAE,MAAM,EAAE,iBAAiB,GAAG,SAAS,GAAG,qBAAqB,EAAE,CAE1H;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAmC/H"}
1
+ {"version":3,"file":"web-host.d.ts","sourceRoot":"","sources":["../src/web-host.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAON,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,MAAM,oBAAoB,CAAC;AAoB5B,OAAO,EAKN,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxF,OAAO,KAAK,EAEX,aAAa,EACb,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,EACxB,gBAAgB,EAIhB,qBAAqB,EACrB,8BAA8B,EAG9B,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,cAAc;IAC9B,SAAS,IAAI,iBAAiB,EAAE,CAAC;IACjC,SAAS,IAAI,iBAAiB,GAAG,SAAS,CAAC;IAC3C,WAAW,IAAI,MAAM,GAAG,SAAS,CAAC;IAClC,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAAC;IAC1C,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IAC3C,YAAY,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG;QACxC,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,gBAAgB,CAAC;QAC1B,WAAW,EAAE,qBAAqB,EAAE,CAAC;KACrC,CAAC;IACF,kBAAkB,IAAI,wBAAwB,CAAC;IAC/C,WAAW,IAAI,kBAAkB,EAAE,CAAC;IACpC,qBAAqB,IAAI,sBAAsB,GAAG,SAAS,CAAC;IAC5D,wBAAwB,IAAI,0BAA0B,CAAC;IACvD,mBAAmB,IAAI,8BAA8B,EAAE,CAAC;IACxD,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,oBAAoB,CAAC;IACjE,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM,EAAE,CAAC;IAC3D,cAAc,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,IAAI,OAAO,CAAC;IAClB,YAAY,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAAC;IACnF,UAAU,IAAI,wBAAwB,CAAC,wBAAwB,CAAC,CAAC;IACjE,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,wBAAwB,CAAC,wBAAwB,CAAC,CAAC;IAClF,YAAY,IAAI,wBAAwB,CAAC,wBAAwB,CAAC,CAAC;CACnE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,cAAc,GAAG,aAAa,CA+CjG;AAwWD,wBAAgB,YAAY,CAAC,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,GAAG,SAAS,GAAG,qBAAqB,CAiBpH;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,EAAE,EAAE,MAAM,EAAE,iBAAiB,GAAG,SAAS,GAAG,qBAAqB,EAAE,CAE1H;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAmC/H"}
package/dist/web-host.js CHANGED
@@ -1,13 +1,30 @@
1
1
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
- import { isInsidePromptStackStorage, promptStackPath, validatePromptStack, } from "./loader.js";
3
+ import { agentProfilePath, agentProfilesDir, isResolvedAgentProfileUsable, validateAgentProfile, validateAgentProfilePromptStackScope, } from "./agent-profile.js";
4
+ import { DEFAULT_SUBAGENT_BACKEND_ID, loadForgeSubagentSettings, resolveSubagentProfilePolicy, updateForgeSubagentProfileConfig, } from "./forge-config.js";
5
+ import { isSafeGlobalPromptStackMutationPath, isSafePromptStackMutationPath, isValidPromptStackId, promptStackPath, validatePromptStack, } from "./loader.js";
6
+ import { globalAgentProfilePath, globalPromptStacksDir } from "./storage.js";
7
+ import { formatResourceKey, parseResourceSelector } from "./resource-identity.js";
8
+ import { resolveResourceSelector } from "./catalog.js";
9
+ import { createAgentProfilePreview, deleteAgentProfile, getAgentProfileRuntimeStatus, writeAgentProfile, } from "./profile-service.js";
4
10
  export function createWebEditorHost(ctx, runtime) {
5
11
  return {
6
12
  cwd: ctx.cwd,
7
13
  listStacks: () => stackSummaries(runtime.getStacks(), runtime.getActive()),
14
+ listProfiles: () => profileCollection(ctx, runtime),
15
+ reloadProfiles: async () => {
16
+ await runtime.reloadProfiles();
17
+ return { ok: true, ...profileCollection(ctx, runtime) };
18
+ },
19
+ validateProfile: (profile, existingId, scope) => profileValidation(ctx, runtime, profile, existingId, scope),
20
+ createProfile: (profile, scope) => createProfileFile(ctx, runtime, profile, scope),
21
+ saveProfile: (selector, profile) => saveProfileFile(ctx, runtime, selector, profile),
22
+ applyProfile: (selector) => applyProfileRuntime(ctx, runtime, selector),
23
+ deleteProfile: (selector) => deleteProfileFile(ctx, runtime, selector),
24
+ updateSubagentPolicy: (selector, update) => updateSubagentPolicy(ctx, runtime, selector, update),
8
25
  listResources: () => runtime.getPolicyResources(),
9
- getStack: (id) => {
10
- const loaded = runtime.getStacks().find((candidate) => candidate.stack.id === id);
26
+ getStack: (selector) => {
27
+ const loaded = resolveStack(runtime, selector);
11
28
  return loaded ? { stack: loaded.stack, filePath: loaded.filePath, diagnostics: loaded.diagnostics } : undefined;
12
29
  },
13
30
  createStack: (stack, options) => createStackFile(ctx, runtime, stack, options),
@@ -15,19 +32,19 @@ export function createWebEditorHost(ctx, runtime) {
15
32
  deleteStack: (id) => deleteStackFile(ctx, runtime, id),
16
33
  validateStack: (stack) => validatePromptStack(stack),
17
34
  previewStack: (id, stack) => {
18
- const target = runtime.getStacks().find((candidate) => candidate.stack.id === id);
35
+ const target = resolveStack(runtime, id);
19
36
  if (!target)
20
37
  return { ok: false, status: 404, error: `Unknown prompt stack: ${id}` };
21
38
  const diagnostics = validatePromptStack(stack);
22
- const preview = runtime.buildPreview({ stack, filePath: target.filePath, diagnostics });
39
+ const preview = runtime.buildPreview({ stack, filePath: target.filePath, scope: target.scope, key: target.key, diagnostics });
23
40
  return { ok: true, text: preview.text, preview: preview.preview, diagnostics: preview.diagnostics };
24
41
  },
25
42
  getPayload: () => runtime.getPayload(),
26
43
  armPayload: (savePath) => runtime.armPayload(savePath),
27
44
  clearPayload: () => runtime.clearPayload(),
28
- activateStack: (id) => {
29
- if (!runtime.setActive(id))
30
- return { ok: false, status: 404, error: `Unknown prompt stack: ${id}` };
45
+ activateStack: (selector) => {
46
+ if (!runtime.setActive(selector))
47
+ return { ok: false, status: 404, error: `Unknown prompt stack: ${selector}` };
31
48
  return { ok: true, activeId: runtime.getActiveId(), stacks: stackSummaries(runtime.getStacks(), runtime.getActive()) };
32
49
  },
33
50
  disableStacks: () => {
@@ -40,11 +57,322 @@ export function createWebEditorHost(ctx, runtime) {
40
57
  },
41
58
  };
42
59
  }
60
+ /** Unqualified selectors address project profiles only; global profiles require `global:<id>`. */
61
+ function resolveProfilesForMutation(runtime, selector) {
62
+ const parsed = parseResourceSelector(selector);
63
+ if (!parsed.ok)
64
+ return [];
65
+ const scope = parsed.selector.scope ?? "project";
66
+ return runtime.getProfiles().filter((loaded) => loaded.scope === scope && loaded.profile.id === parsed.selector.id);
67
+ }
68
+ function profileCollection(ctx, runtime) {
69
+ const profiles = runtime.getProfiles();
70
+ const current = runtime.getCurrentProfileRuntime();
71
+ const lastApplied = runtime.getLastAppliedProfile();
72
+ const availableModels = new Set(ctx.modelRegistry.getAvailable().map((model) => modelKey(model.provider, model.id)));
73
+ const subagentSettings = loadForgeSubagentSettings(ctx);
74
+ const subagentBackends = runtime.getSubagentBackends();
75
+ const registeredBackends = new Set(subagentBackends.map((backend) => backend.id));
76
+ return {
77
+ trusted: ctx.isProjectTrusted(),
78
+ profileDirectory: agentProfilesDir(ctx.cwd),
79
+ profiles: profiles.map((loaded) => {
80
+ const resolved = runtime.resolveProfile(loaded);
81
+ const targetEffectiveTools = resolved.promptStack || resolved.loaded.profile.promptStack === null
82
+ ? runtime.previewToolNames(resolved.promptStack?.stack)
83
+ : [];
84
+ const preview = createAgentProfilePreview(resolved, current, targetEffectiveTools);
85
+ return {
86
+ profile: structuredClone(loaded.profile),
87
+ filePath: loaded.filePath,
88
+ selector: formatResourceKey(loaded.key),
89
+ scope: loaded.scope,
90
+ preview,
91
+ errors: preview.diagnostics.filter((diagnostic) => diagnostic.level === "error").length,
92
+ warnings: preview.diagnostics.filter((diagnostic) => diagnostic.level === "warning").length,
93
+ lastApplied: lastApplied?.sourcePath === loaded.filePath,
94
+ subagent: subagentPolicySummary(subagentSettings, loaded, registeredBackends),
95
+ };
96
+ }),
97
+ status: getAgentProfileRuntimeStatus(profiles, lastApplied, current),
98
+ models: ctx.modelRegistry.getAll().map((model) => ({
99
+ provider: model.provider,
100
+ id: model.id,
101
+ name: model.name,
102
+ available: availableModels.has(modelKey(model.provider, model.id)),
103
+ })),
104
+ promptStacks: runtime.getStacks().map((loaded) => ({
105
+ id: loaded.stack.id,
106
+ name: loaded.stack.name,
107
+ selector: formatResourceKey(loaded.key),
108
+ scope: loaded.scope,
109
+ })),
110
+ subagents: {
111
+ defaultBackend: subagentSettings.backend ?? DEFAULT_SUBAGENT_BACKEND_ID,
112
+ defaultBackendSource: subagentSettings.backendSource ?? "built-in",
113
+ timeoutMs: subagentSettings.timeoutMs,
114
+ timeoutSource: subagentSettings.timeoutSource,
115
+ allowAgentInvocationWithoutApproval: subagentSettings.allowAgentInvocationWithoutApproval,
116
+ backends: subagentBackends,
117
+ warnings: [...subagentSettings.warnings],
118
+ },
119
+ };
120
+ }
121
+ function subagentPolicySummary(settings, loaded, registeredBackends) {
122
+ const selector = formatResourceKey(loaded.key);
123
+ const policy = resolveSubagentProfilePolicy(settings, selector);
124
+ const configured = settings.profiles[selector];
125
+ return {
126
+ enabled: policy.enabled,
127
+ enabledSource: policy.enabledSource,
128
+ backend: policy.backend.id,
129
+ backendSource: policy.backend.source,
130
+ backendRegistered: registeredBackends.has(policy.backend.id),
131
+ timeoutMs: policy.timeout.milliseconds,
132
+ timeoutSource: policy.timeout.source,
133
+ configured: configuredSubagentProfile(configured),
134
+ };
135
+ }
136
+ function configuredSubagentProfile(configured) {
137
+ if (!configured)
138
+ return {};
139
+ return {
140
+ enabled: configured.enabled,
141
+ backend: configured.backend,
142
+ timeoutMs: configured.timeoutMs,
143
+ };
144
+ }
145
+ function profileValidation(ctx, runtime, profile, existingId, validationScope) {
146
+ const profiles = runtime.getProfiles();
147
+ const existingSelector = existingId ? parseResourceSelector(existingId) : undefined;
148
+ const existingMatches = existingSelector?.ok
149
+ ? profiles.filter((loaded) => loaded.scope === (existingSelector.selector.scope ?? "project") && loaded.profile.id === existingSelector.selector.id)
150
+ : [];
151
+ const existing = existingMatches.length === 1 ? existingMatches[0] : undefined;
152
+ const scope = existing?.scope ?? validationScope ?? "project";
153
+ const filePath = existing?.filePath ?? (scope === "global" ? globalAgentProfilePath(profile.id) : agentProfilePath(ctx.cwd, profile.id));
154
+ const diagnostics = [
155
+ ...validateAgentProfile(profile),
156
+ ...validateAgentProfilePromptStackScope(profile, scope),
157
+ ];
158
+ const otherProfiles = existing
159
+ ? profiles.filter((loaded) => loaded.scope === scope && loaded.filePath !== filePath)
160
+ : profiles.filter((loaded) => loaded.scope === scope);
161
+ if (otherProfiles.some((loaded) => loaded.profile.id === profile.id)) {
162
+ diagnostics.push({
163
+ level: "error",
164
+ field: "id",
165
+ message: `Duplicate profile id: ${profile.id} already exists in another file.`,
166
+ });
167
+ }
168
+ if (!existing && existsSync(filePath)) {
169
+ diagnostics.push({
170
+ level: "error",
171
+ field: "id",
172
+ message: `Profile file already exists: ${filePath}`,
173
+ });
174
+ }
175
+ if (profile.autoActivate && otherProfiles.some((loaded) => loaded.profile.autoActivate === true)) {
176
+ diagnostics.push({
177
+ level: "error",
178
+ field: "autoActivate",
179
+ message: "Multiple profiles request auto-activation; exactly one is allowed.",
180
+ });
181
+ }
182
+ const resolved = runtime.resolveProfile({ profile, filePath, scope, key: { scope, id: profile.id }, diagnostics });
183
+ const targetEffectiveTools = resolved.promptStack || profile.promptStack === null
184
+ ? runtime.previewToolNames(resolved.promptStack?.stack)
185
+ : [];
186
+ const preview = createAgentProfilePreview(resolved, runtime.getCurrentProfileRuntime(), targetEffectiveTools);
187
+ return {
188
+ preview,
189
+ diagnostics: preview.diagnostics,
190
+ errors: preview.diagnostics.filter((diagnostic) => diagnostic.level === "error").length,
191
+ warnings: preview.diagnostics.filter((diagnostic) => diagnostic.level === "warning").length,
192
+ };
193
+ }
194
+ async function createProfileFile(ctx, runtime, profile, scope = "project") {
195
+ if (!ctx.isProjectTrusted()) {
196
+ return { ok: false, status: 403, error: "Project is not trusted; refusing to create agent profiles." };
197
+ }
198
+ if (runtime.getProfiles().some((loaded) => loaded.scope === scope && loaded.profile.id === profile.id)) {
199
+ return { ok: false, status: 409, error: `Agent profile already exists: ${profile.id}` };
200
+ }
201
+ const conflict = autoActivateConflictError(runtime.getProfiles(), profile, scope);
202
+ if (conflict)
203
+ return { ok: false, status: 409, error: conflict };
204
+ const result = writeAgentProfile(ctx.cwd, profile, { scope });
205
+ if (!result.ok)
206
+ return profileWriteError(profile.id, result);
207
+ await runtime.reloadProfiles();
208
+ return {
209
+ ok: true,
210
+ collection: profileCollection(ctx, runtime),
211
+ selectedPath: result.filePath,
212
+ };
213
+ }
214
+ async function saveProfileFile(ctx, runtime, id, profile) {
215
+ if (!ctx.isProjectTrusted()) {
216
+ return { ok: false, status: 403, error: "Project is not trusted; refusing to save agent profiles." };
217
+ }
218
+ const matches = resolveProfilesForMutation(runtime, id);
219
+ if (matches.length === 0)
220
+ return { ok: false, status: 404, error: `Unknown agent profile: ${id}` };
221
+ if (matches.length > 1) {
222
+ return { ok: false, status: 409, error: `Cannot save agent profile ${id} while duplicate profile ids exist.` };
223
+ }
224
+ if (profile.id !== matches[0].profile.id) {
225
+ return { ok: false, status: 400, error: "Profile id is immutable during save; create a new profile to use a different id." };
226
+ }
227
+ const conflict = autoActivateConflictError(runtime.getProfiles(), profile, matches[0].scope, matches[0].profile.id);
228
+ if (conflict)
229
+ return { ok: false, status: 409, error: conflict };
230
+ const result = writeAgentProfile(ctx.cwd, profile, {
231
+ filePath: matches[0].filePath,
232
+ overwrite: true,
233
+ scope: matches[0].scope,
234
+ });
235
+ if (!result.ok)
236
+ return profileWriteError(id, result);
237
+ await runtime.reloadProfiles();
238
+ return {
239
+ ok: true,
240
+ collection: profileCollection(ctx, runtime),
241
+ selectedPath: result.filePath,
242
+ };
243
+ }
244
+ async function updateSubagentPolicy(ctx, runtime, id, update) {
245
+ if (!ctx.isProjectTrusted()) {
246
+ return { ok: false, status: 403, error: "Project is not trusted; refusing to change subagent delegation." };
247
+ }
248
+ const matches = resolveProfilesForMutation(runtime, id);
249
+ if (matches.length === 0)
250
+ return { ok: false, status: 404, error: `Unknown agent profile: ${id}` };
251
+ if (matches.length > 1) {
252
+ return { ok: false, status: 409, error: `Cannot configure subagent delegation for ${id} while duplicate profile ids exist.` };
253
+ }
254
+ const result = updateForgeSubagentProfileConfig(ctx.cwd, matches[0].profile.id, update, { scope: matches[0].scope });
255
+ if (!result.ok)
256
+ return { ok: false, status: 400, error: result.error };
257
+ return {
258
+ ok: true,
259
+ collection: profileCollection(ctx, runtime),
260
+ selectedPath: matches[0].filePath,
261
+ };
262
+ }
263
+ function autoActivateConflictError(profiles, profile, scope, excludeId) {
264
+ if (profile.autoActivate !== true)
265
+ return undefined;
266
+ const conflict = profiles.find((loaded) => loaded.scope === scope && loaded.profile.autoActivate === true && loaded.profile.id !== excludeId);
267
+ return conflict
268
+ ? `Multiple ${scope} profiles request auto-activation; exactly one is allowed (already requested by ${conflict.profile.id}).`
269
+ : undefined;
270
+ }
271
+ async function applyProfileRuntime(ctx, runtime, id) {
272
+ if (!ctx.isProjectTrusted()) {
273
+ return { ok: false, status: 403, error: "Project is not trusted; refusing to apply agent profiles." };
274
+ }
275
+ if (!runtime.isIdle()) {
276
+ return { ok: false, status: 409, error: "Wait for the current agent operation to finish before applying a profile." };
277
+ }
278
+ const matches = resolveProfilesForMutation(runtime, id);
279
+ if (matches.length === 0)
280
+ return { ok: false, status: 404, error: `Unknown agent profile: ${id}` };
281
+ if (matches.length > 1) {
282
+ return { ok: false, status: 409, error: `Cannot apply agent profile ${id} while duplicate profile ids exist.` };
283
+ }
284
+ const resolved = runtime.resolveProfile(matches[0]);
285
+ if (!isResolvedAgentProfileUsable(resolved) || !resolved.model) {
286
+ return { ok: false, status: 400, error: `Agent profile ${id} failed preflight; runtime state was not changed.` };
287
+ }
288
+ const result = await runtime.applyProfile(resolved);
289
+ if (!result.ok) {
290
+ const rollback = result.rollbackErrors.length
291
+ ? ` Rollback problems: ${result.rollbackErrors.join("; ")}.`
292
+ : " Previous runtime state was restored.";
293
+ return { ok: false, status: 500, error: `${result.detail}${result.detail.endsWith(".") ? "" : "."}${rollback}` };
294
+ }
295
+ return {
296
+ ok: true,
297
+ collection: profileCollection(ctx, runtime),
298
+ selectedPath: matches[0].filePath,
299
+ };
300
+ }
301
+ async function deleteProfileFile(ctx, runtime, id) {
302
+ if (!ctx.isProjectTrusted()) {
303
+ return { ok: false, status: 403, error: "Project is not trusted; refusing to delete agent profiles." };
304
+ }
305
+ const matches = resolveProfilesForMutation(runtime, id);
306
+ if (matches.length === 0)
307
+ return { ok: false, status: 404, error: `Unknown agent profile: ${id}` };
308
+ if (matches.length > 1) {
309
+ return { ok: false, status: 409, error: `Cannot delete agent profile ${id} while duplicate profile ids exist.` };
310
+ }
311
+ const subagentSettings = loadForgeSubagentSettings(ctx);
312
+ const result = deleteAgentProfile(ctx.cwd, matches[0]);
313
+ if (!result.ok) {
314
+ if (result.reason === "invalid-path")
315
+ return { ok: false, status: 403, error: "Refusing to delete outside agent-profile storage." };
316
+ if (result.reason === "missing")
317
+ return { ok: false, status: 404, error: `Agent profile file is missing: ${result.filePath}` };
318
+ if (result.reason === "changed") {
319
+ return { ok: false, status: 409, error: `Agent profile ${id} changed on disk; refresh before deleting it.` };
320
+ }
321
+ return { ok: false, status: 500, error: result.error ?? `Failed to delete agent profile ${id}.` };
322
+ }
323
+ if (Object.hasOwn(subagentSettings.profiles, formatResourceKey(matches[0].key))) {
324
+ const cleared = updateForgeSubagentProfileConfig(ctx.cwd, matches[0].profile.id, {
325
+ enabled: false,
326
+ backend: null,
327
+ timeoutMs: null,
328
+ }, { scope: matches[0].scope });
329
+ if (!cleared.ok) {
330
+ await runtime.reloadProfiles();
331
+ return {
332
+ ok: false,
333
+ status: 500,
334
+ error: `Deleted agent profile ${id}, but failed to remove its delegation settings: ${cleared.error}`,
335
+ };
336
+ }
337
+ }
338
+ await runtime.reloadProfiles();
339
+ const collection = profileCollection(ctx, runtime);
340
+ return {
341
+ ok: true,
342
+ collection,
343
+ selectedPath: collection.profiles[0]?.filePath ?? "",
344
+ };
345
+ }
346
+ function profileWriteError(id, result) {
347
+ if (result.reason === "exists")
348
+ return { ok: false, status: 409, error: `Agent profile already exists: ${id}` };
349
+ if (result.reason === "invalid-path")
350
+ return { ok: false, status: 403, error: result.error ?? "Refusing to write outside agent-profile storage." };
351
+ if (result.reason === "validation") {
352
+ return {
353
+ ok: false,
354
+ status: 400,
355
+ error: result.diagnostics.map((diagnostic) => diagnostic.message).join(" "),
356
+ };
357
+ }
358
+ return { ok: false, status: 500, error: result.error ?? `Failed to write agent profile ${id}.` };
359
+ }
360
+ function modelKey(provider, id) {
361
+ return `${provider}\0${id}`;
362
+ }
363
+ function resolveStack(runtime, selector) {
364
+ const parsed = parseResourceSelector(selector);
365
+ if (!parsed.ok)
366
+ return undefined;
367
+ return resolveResourceSelector(runtime.getStacks(), parsed.selector);
368
+ }
43
369
  export function stackSummary(loaded, active) {
44
370
  const errors = loaded.diagnostics.filter((d) => d.level === "error").length;
45
371
  const warnings = loaded.diagnostics.filter((d) => d.level === "warning").length;
46
372
  return {
47
373
  id: loaded.stack.id,
374
+ selector: formatResourceKey(loaded.key),
375
+ scope: loaded.scope,
48
376
  name: loaded.stack.name,
49
377
  filePath: loaded.filePath,
50
378
  active: loaded === active,
@@ -96,22 +424,26 @@ async function saveStackFile(ctx, runtime, id, stack) {
96
424
  if (!ctx.isProjectTrusted()) {
97
425
  return { ok: false, status: 403, error: "Project is not trusted; refusing to save prompt stacks." };
98
426
  }
99
- const target = runtime.getStacks().find((candidate) => candidate.stack.id === id);
427
+ const target = resolveStack(runtime, id);
100
428
  if (!target)
101
429
  return { ok: false, status: 404, error: `Unknown prompt stack: ${id}` };
102
430
  const idError = validateWebStackId(stack.id);
103
431
  if (idError)
104
432
  return { ok: false, status: 400, error: idError };
105
- if (stack.id !== id) {
433
+ if (stack.id !== target.stack.id) {
106
434
  return { ok: false, status: 400, error: "Stack id is immutable during save; fork the stack to create a new id." };
107
435
  }
108
- if (!isInsidePromptStackStorage(ctx.cwd, target.filePath)) {
109
- return { ok: false, status: 403, error: "Refusing to save outside prompt-stack storage." };
436
+ const safe = target.scope === "project"
437
+ ? isSafePromptStackMutationPath(ctx.cwd, target.filePath)
438
+ : isSafeGlobalPromptStackMutationPath(target.filePath);
439
+ if (!safe) {
440
+ return { ok: false, status: 403, error: "Refusing to save outside prompt-stack storage or through a symbolic link." };
110
441
  }
111
442
  writeFileSync(target.filePath, `${JSON.stringify(stack, null, 2)}\n`, "utf8");
112
- const preferredId = runtime.getActive()?.stack.id === id ? stack.id : runtime.getSelectedActiveId();
443
+ const preferredId = runtime.getActive() === target ? formatResourceKey(target.key) : runtime.getSelectedActiveId();
113
444
  await runtime.reloadStacks(preferredId);
114
- const saved = runtime.getStacks().find((candidate) => candidate.stack.id === stack.id) ?? runtime.getStacks().find((candidate) => candidate.filePath === target.filePath);
445
+ const saved = runtime.getStacks().find((candidate) => candidate.scope === target.scope && candidate.stack.id === target.stack.id)
446
+ ?? runtime.getStacks().find((candidate) => candidate.filePath === target.filePath);
115
447
  if (!saved)
116
448
  return { ok: false, status: 500, error: "Saved stack could not be reloaded." };
117
449
  return { ok: true, stack: stackSummary(saved, runtime.getActive()), stacks: stackSummaries(runtime.getStacks(), runtime.getActive()) };
@@ -123,24 +455,33 @@ async function createStackFile(ctx, runtime, stack, options) {
123
455
  const idError = validateWebStackId(stack.id);
124
456
  if (idError)
125
457
  return { ok: false, status: 400, error: idError };
126
- const existingById = runtime.getStacks().find((candidate) => candidate.stack.id === stack.id);
458
+ const scope = options.scope ?? "project";
459
+ const existingById = runtime.getStacks().find((candidate) => candidate.scope === scope && candidate.stack.id === stack.id);
127
460
  if (existingById && !options.overwrite) {
128
461
  return { ok: false, status: 409, error: `Prompt stack already exists: ${stack.id}` };
129
462
  }
130
- const targetPath = existingById && options.overwrite ? existingById.filePath : promptStackPath(ctx.cwd, stack.id);
131
- if (!isInsidePromptStackStorage(ctx.cwd, targetPath)) {
132
- return { ok: false, status: 403, error: "Refusing to create outside prompt-stack storage." };
463
+ const targetPath = existingById && options.overwrite
464
+ ? existingById.filePath
465
+ : scope === "global"
466
+ ? join(globalPromptStacksDir(), `${stack.id}.json`)
467
+ : promptStackPath(ctx.cwd, stack.id);
468
+ const safe = scope === "project"
469
+ ? isSafePromptStackMutationPath(ctx.cwd, targetPath)
470
+ : isSafeGlobalPromptStackMutationPath(targetPath);
471
+ if (!safe) {
472
+ return { ok: false, status: 403, error: `Refusing to create outside ${scope} prompt-stack storage or through a symbolic link.` };
133
473
  }
134
474
  if (!existingById && existsSync(targetPath) && !options.overwrite) {
135
475
  return { ok: false, status: 409, error: `Prompt stack already exists: ${stack.id}` };
136
476
  }
137
477
  const previousSelection = runtime.getSelectedActiveId();
478
+ const createdSelector = formatResourceKey({ scope, id: stack.id });
138
479
  mkdirSync(dirname(targetPath), { recursive: true });
139
480
  writeFileSync(targetPath, `${JSON.stringify(stack, null, 2)}\n`, "utf8");
140
- await runtime.reloadStacks(options.activate ? stack.id : (previousSelection ?? "none"));
481
+ await runtime.reloadStacks(options.activate ? createdSelector : (previousSelection ?? "none"));
141
482
  if (options.activate)
142
- runtime.setActive(stack.id);
143
- const created = runtime.getStacks().find((candidate) => candidate.stack.id === stack.id);
483
+ runtime.setActive(createdSelector);
484
+ const created = runtime.getStacks().find((candidate) => candidate.scope === scope && candidate.filePath === targetPath);
144
485
  if (!created)
145
486
  return { ok: false, status: 500, error: "Created stack could not be reloaded." };
146
487
  return { ok: true, stack: stackSummary(created, runtime.getActive()), stacks: stackSummaries(runtime.getStacks(), runtime.getActive()) };
@@ -149,13 +490,16 @@ async function deleteStackFile(ctx, runtime, id) {
149
490
  if (!ctx.isProjectTrusted()) {
150
491
  return { ok: false, status: 403, error: "Project is not trusted; refusing to delete prompt stacks." };
151
492
  }
152
- const target = runtime.getStacks().find((candidate) => candidate.stack.id === id);
493
+ const target = resolveStack(runtime, id);
153
494
  if (!target)
154
495
  return { ok: false, status: 404, error: `Unknown prompt stack: ${id}` };
155
- if (!isInsidePromptStackStorage(ctx.cwd, target.filePath)) {
156
- return { ok: false, status: 403, error: "Refusing to delete outside prompt-stack storage." };
496
+ const safe = target.scope === "project"
497
+ ? isSafePromptStackMutationPath(ctx.cwd, target.filePath)
498
+ : isSafeGlobalPromptStackMutationPath(target.filePath);
499
+ if (!safe) {
500
+ return { ok: false, status: 403, error: "Refusing to delete outside prompt-stack storage or through a symbolic link." };
157
501
  }
158
- const wasActive = runtime.getActive()?.stack.id === id;
502
+ const wasActive = runtime.getActive() === target;
159
503
  unlinkSync(target.filePath);
160
504
  if (wasActive) {
161
505
  runtime.setActive("none");
@@ -169,8 +513,8 @@ async function deleteStackFile(ctx, runtime, id) {
169
513
  function validateWebStackId(id) {
170
514
  if (!id.trim())
171
515
  return "Stack id must not be empty.";
172
- if (!/^[A-Za-z0-9_-]+$/.test(id)) {
173
- return "Stack id may only contain letters, numbers, underscore, and dash.";
516
+ if (!isValidPromptStackId(id)) {
517
+ return "Stack id must start with a letter or number and contain only letters, numbers, dots, underscores, and hyphens.";
174
518
  }
175
519
  return undefined;
176
520
  }