@ngocsangairvds/vsaf 5.1.17 → 5.2.0

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 (137) hide show
  1. package/README.md +55 -1
  2. package/package.json +1 -1
  3. package/packages/cli/dist/commands/abandon.d.ts +6 -0
  4. package/packages/cli/dist/commands/abandon.d.ts.map +1 -0
  5. package/packages/cli/dist/commands/abandon.js +42 -0
  6. package/packages/cli/dist/commands/abandon.js.map +1 -0
  7. package/packages/cli/dist/commands/ai.d.ts +21 -0
  8. package/packages/cli/dist/commands/ai.d.ts.map +1 -0
  9. package/packages/cli/dist/commands/ai.js +76 -0
  10. package/packages/cli/dist/commands/ai.js.map +1 -0
  11. package/packages/cli/dist/commands/approve.d.ts +19 -0
  12. package/packages/cli/dist/commands/approve.d.ts.map +1 -0
  13. package/packages/cli/dist/commands/approve.js +115 -0
  14. package/packages/cli/dist/commands/approve.js.map +1 -0
  15. package/packages/cli/dist/commands/chat.d.ts +41 -0
  16. package/packages/cli/dist/commands/chat.d.ts.map +1 -0
  17. package/packages/cli/dist/commands/chat.js +129 -0
  18. package/packages/cli/dist/commands/chat.js.map +1 -0
  19. package/packages/cli/dist/commands/isolation.d.ts +20 -0
  20. package/packages/cli/dist/commands/isolation.d.ts.map +1 -0
  21. package/packages/cli/dist/commands/isolation.js +104 -0
  22. package/packages/cli/dist/commands/isolation.js.map +1 -0
  23. package/packages/cli/dist/commands/run.d.ts +36 -1
  24. package/packages/cli/dist/commands/run.d.ts.map +1 -1
  25. package/packages/cli/dist/commands/run.js +161 -15
  26. package/packages/cli/dist/commands/run.js.map +1 -1
  27. package/packages/cli/dist/commands/telemetry.d.ts +10 -0
  28. package/packages/cli/dist/commands/telemetry.d.ts.map +1 -0
  29. package/packages/cli/dist/commands/telemetry.js +51 -0
  30. package/packages/cli/dist/commands/telemetry.js.map +1 -0
  31. package/packages/cli/dist/commands/validate.d.ts +12 -0
  32. package/packages/cli/dist/commands/validate.d.ts.map +1 -0
  33. package/packages/cli/dist/commands/validate.js +83 -0
  34. package/packages/cli/dist/commands/validate.js.map +1 -0
  35. package/packages/cli/dist/commands/workflow.d.ts +29 -0
  36. package/packages/cli/dist/commands/workflow.d.ts.map +1 -0
  37. package/packages/cli/dist/commands/workflow.js +147 -0
  38. package/packages/cli/dist/commands/workflow.js.map +1 -0
  39. package/packages/cli/dist/index.js +253 -6
  40. package/packages/cli/dist/index.js.map +1 -1
  41. package/packages/cli/dist/mcp/server.d.ts +9 -0
  42. package/packages/cli/dist/mcp/server.d.ts.map +1 -1
  43. package/packages/cli/dist/mcp/server.js +38 -2
  44. package/packages/cli/dist/mcp/server.js.map +1 -1
  45. package/packages/cli/dist/server/app.d.ts.map +1 -1
  46. package/packages/cli/dist/server/app.js +2 -0
  47. package/packages/cli/dist/server/app.js.map +1 -1
  48. package/packages/cli/dist/server/routes/conversations.d.ts +6 -0
  49. package/packages/cli/dist/server/routes/conversations.d.ts.map +1 -0
  50. package/packages/cli/dist/server/routes/conversations.js +145 -0
  51. package/packages/cli/dist/server/routes/conversations.js.map +1 -0
  52. package/packages/cli/dist/server/routes/runs.d.ts.map +1 -1
  53. package/packages/cli/dist/server/routes/runs.js +15 -3
  54. package/packages/cli/dist/server/routes/runs.js.map +1 -1
  55. package/packages/cli/dist/server/services/execution-manager.d.ts +25 -0
  56. package/packages/cli/dist/server/services/execution-manager.d.ts.map +1 -1
  57. package/packages/cli/dist/server/services/execution-manager.js +255 -95
  58. package/packages/cli/dist/server/services/execution-manager.js.map +1 -1
  59. package/packages/core/dist/config/ai-config.d.ts +24 -0
  60. package/packages/core/dist/config/ai-config.d.ts.map +1 -0
  61. package/packages/core/dist/config/ai-config.js +170 -0
  62. package/packages/core/dist/config/ai-config.js.map +1 -0
  63. package/packages/core/dist/engine/dag-executor.d.ts +18 -0
  64. package/packages/core/dist/engine/dag-executor.d.ts.map +1 -1
  65. package/packages/core/dist/engine/dag-executor.js +181 -8
  66. package/packages/core/dist/engine/dag-executor.js.map +1 -1
  67. package/packages/core/dist/engine/dag-parser.d.ts.map +1 -1
  68. package/packages/core/dist/engine/dag-parser.js +32 -1
  69. package/packages/core/dist/engine/dag-parser.js.map +1 -1
  70. package/packages/core/dist/engine/loop-signal.d.ts +7 -0
  71. package/packages/core/dist/engine/loop-signal.d.ts.map +1 -0
  72. package/packages/core/dist/engine/loop-signal.js +21 -0
  73. package/packages/core/dist/engine/loop-signal.js.map +1 -0
  74. package/packages/core/dist/engine/node-runner.d.ts +4 -1
  75. package/packages/core/dist/engine/node-runner.d.ts.map +1 -1
  76. package/packages/core/dist/engine/node-runner.js +14 -6
  77. package/packages/core/dist/engine/node-runner.js.map +1 -1
  78. package/packages/core/dist/index.d.ts +5 -2
  79. package/packages/core/dist/index.d.ts.map +1 -1
  80. package/packages/core/dist/index.js +25 -1
  81. package/packages/core/dist/index.js.map +1 -1
  82. package/packages/core/dist/runtime/executor.d.ts +1 -1
  83. package/packages/core/dist/runtime/executor.d.ts.map +1 -1
  84. package/packages/core/dist/runtime/executor.js +12 -8
  85. package/packages/core/dist/runtime/executor.js.map +1 -1
  86. package/packages/core/dist/schema/workflow-schema.d.ts +94 -14
  87. package/packages/core/dist/schema/workflow-schema.d.ts.map +1 -1
  88. package/packages/core/dist/schema/workflow-schema.js +38 -6
  89. package/packages/core/dist/schema/workflow-schema.js.map +1 -1
  90. package/packages/core/dist/store/recovery.d.ts.map +1 -1
  91. package/packages/core/dist/store/recovery.js +7 -0
  92. package/packages/core/dist/store/recovery.js.map +1 -1
  93. package/packages/core/dist/store/run-store.d.ts +5 -3
  94. package/packages/core/dist/store/run-store.d.ts.map +1 -1
  95. package/packages/core/dist/store/run-store.js +12 -4
  96. package/packages/core/dist/store/run-store.js.map +1 -1
  97. package/packages/core/dist/store/sqlite-schema.d.ts +1 -1
  98. package/packages/core/dist/store/sqlite-schema.d.ts.map +1 -1
  99. package/packages/core/dist/store/sqlite-schema.js +20 -0
  100. package/packages/core/dist/store/sqlite-schema.js.map +1 -1
  101. package/packages/core/dist/store/sqlite-workflow-store.d.ts +26 -0
  102. package/packages/core/dist/store/sqlite-workflow-store.d.ts.map +1 -1
  103. package/packages/core/dist/store/sqlite-workflow-store.js +106 -0
  104. package/packages/core/dist/store/sqlite-workflow-store.js.map +1 -1
  105. package/packages/core/dist/store/types.d.ts +15 -0
  106. package/packages/core/dist/store/types.d.ts.map +1 -1
  107. package/packages/core/dist/store/types.js.map +1 -1
  108. package/packages/core/dist/telemetry/telemetry.d.ts +31 -0
  109. package/packages/core/dist/telemetry/telemetry.d.ts.map +1 -0
  110. package/packages/core/dist/telemetry/telemetry.js +177 -0
  111. package/packages/core/dist/telemetry/telemetry.js.map +1 -0
  112. package/packages/core/dist/types/dag.d.ts +16 -0
  113. package/packages/core/dist/types/dag.d.ts.map +1 -1
  114. package/packages/core/dist/types/workflow.d.ts +18 -1
  115. package/packages/core/dist/types/workflow.d.ts.map +1 -1
  116. package/packages/web/dist/web/browser/chunk-2JHYGCCN.js +1 -0
  117. package/packages/web/dist/web/browser/chunk-BMQT4ZPK.js +1 -0
  118. package/packages/web/dist/web/browser/chunk-GWHJRWJM.js +1 -0
  119. package/packages/web/dist/web/browser/chunk-OZ6BRCXC.js +1 -0
  120. package/packages/web/dist/web/browser/chunk-Q7N6DAKT.js +1 -0
  121. package/packages/web/dist/web/browser/chunk-SEBU2SK7.js +1 -0
  122. package/packages/web/dist/web/browser/chunk-SOS4TH5D.js +4 -0
  123. package/packages/web/dist/web/browser/chunk-WSAVMPNK.js +5 -0
  124. package/packages/web/dist/web/browser/index.html +1 -1
  125. package/packages/web/dist/web/browser/main-UPZIVGM6.js +1 -0
  126. package/workflows/sdlc/hotfix-tdd.yaml +1 -1
  127. package/workflows/sdlc/hotfix.yaml +7 -4
  128. package/workflows/sdlc/master-sdlc.yaml +18 -9
  129. package/packages/web/dist/web/browser/chunk-5WD7R336.js +0 -1
  130. package/packages/web/dist/web/browser/chunk-FXYDTGOX.js +0 -1
  131. package/packages/web/dist/web/browser/chunk-K4DHLOOA.js +0 -1
  132. package/packages/web/dist/web/browser/chunk-V2CDNNWN.js +0 -1
  133. package/packages/web/dist/web/browser/chunk-WJT4QH2P.js +0 -4
  134. package/packages/web/dist/web/browser/chunk-WMJLW2L6.js +0 -5
  135. package/packages/web/dist/web/browser/chunk-XHOL7AER.js +0 -1
  136. package/packages/web/dist/web/browser/chunk-YBYFKZWR.js +0 -1
  137. package/packages/web/dist/web/browser/main-SYHKSBNJ.js +0 -1
package/README.md CHANGED
@@ -142,11 +142,20 @@ vsaf serve --port 3000 --open
142
142
  | `vsaf run <workflow> --resume <runId>` | Resume an interrupted run |
143
143
  | `vsaf list` | List available workflows |
144
144
  | `vsaf status` | Show recent run statuses |
145
+ | `vsaf runs` | List workflow runs (alias of `status`) |
146
+ | `vsaf chat "<message>"` | Send a message to a conversation; a paused run in it resumes with the message as gate feedback (approve/reject keywords, anything else is revision feedback for looped gates) |
147
+ | `vsaf abandon <runId>` | Mark a run cancelled without deleting its data |
148
+ | `vsaf isolation list` | List `.vsaf/worktrees/` entries with their run status |
149
+ | `vsaf isolation cleanup [--merged] [--dry-run]` | Remove worktrees of terminal/orphaned runs; `--merged` also reclaims paused runs whose branch is merged (never removes executing runs) |
150
+ | `vsaf validate workflows [name]` | Validate workflow YAML: schema, node references, command file existence |
145
151
  | `vsaf doctor` | Check environment and dependencies |
146
152
  | `vsaf serve` | Start API server and web dashboard |
147
153
  | `vsaf mcp` | Start MCP server for Claude Code integration |
148
154
  | `vsaf install <pack>` | Install a skill pack globally (e.g. `vsaf install sdlc`) |
149
155
  | `vsaf skill` | Manage skills: `add`, `list`, `info`, `remove`, `init` |
156
+ | `vsaf ai` | Manage model tiers and aliases: `tier set\|list\|unset`, `alias set\|list\|unset` |
157
+ | `vsaf workflow` | Community marketplace: `search [query]`, `install <slug>` |
158
+ | `vsaf telemetry` | Anonymous usage telemetry: `status`, `reset` |
150
159
 
151
160
  ### `vsaf serve`
152
161
 
@@ -176,6 +185,50 @@ vsaf skill remove <name> # Remove a skill
176
185
  vsaf skill init # Scaffold a new skill template
177
186
  ```
178
187
 
188
+ ### `vsaf ai` — model tiers and aliases
189
+
190
+ ```bash
191
+ vsaf ai tier set small claude haiku # Bind a tier to provider/model
192
+ vsaf ai tier list # Show tiers (configured + built-in)
193
+ vsaf ai tier unset small # Remove a tier binding
194
+ vsaf ai alias set @cheap openai-api gpt-4o-mini
195
+ vsaf ai alias list
196
+ vsaf ai alias unset @cheap
197
+ ```
198
+
199
+ Bindings persist in `~/.vsaf/config.yaml` under `tiers:` and `aliases:`.
200
+ Workflow nodes (and `vsaf run --model`) may then use `model: small|medium|large`
201
+ or `model: "@alias"` — resolved to a concrete provider+model at run time, with a
202
+ tier fallback chain (`large → medium → small`) and built-in claude defaults
203
+ (haiku/sonnet/opus).
204
+
205
+ ### `vsaf workflow` — community marketplace
206
+
207
+ ```bash
208
+ vsaf workflow search review # Search the registry index
209
+ vsaf workflow install review-pr # Install into .vsaf/workflows/community/
210
+ vsaf run community/review-pr # Run an installed workflow
211
+ ```
212
+
213
+ The registry is a git repo of YAML workflows described by a JSON index.
214
+ Override the index URL with `--registry <url>`, `VSAF_WORKFLOW_REGISTRY`, or
215
+ `marketplace.index_url` in `~/.vsaf/config.yaml`. Downloads are validated
216
+ against the workflow schema before being installed.
217
+
218
+ ### `vsaf telemetry`
219
+
220
+ ```bash
221
+ vsaf telemetry status # enabled/disabled + reason + install id
222
+ vsaf telemetry reset # rotate the anonymous install id
223
+ ```
224
+
225
+ Anonymous, opt-out usage telemetry: workflow name (bundled workflows only —
226
+ user workflows report as `custom`), node type counts, provider/model, and run
227
+ outcome. Never prompts, code, arguments, or file paths. Disabled when
228
+ `DO_NOT_TRACK=1`, `VSAF_TELEMETRY_DISABLED=1`, in CI, or when no endpoint is
229
+ configured (`telemetry.endpoint` in `~/.vsaf/config.yaml` or
230
+ `VSAF_TELEMETRY_ENDPOINT`). The install id lives in `~/.vsaf/telemetry-id`.
231
+
179
232
  ---
180
233
 
181
234
  ## Writing Workflows
@@ -233,9 +286,10 @@ vsaf run my-workflow --args "analyze the auth module"
233
286
  | `when` | no | Condition expression — node skipped if false |
234
287
  | `trigger_rule` | no | `all_success` (default) or `one_success` |
235
288
  | `context` | no | `fresh` (new AI session) or `inherit` (default) |
236
- | `model` | no | Override the workflow-level model |
289
+ | `model` | no | Override the workflow-level model — literal, tier (`small`/`medium`/`large`), or `@alias` |
237
290
  | `allowed_tools` | no | Restrict AI tool access (empty array = no tools) |
238
291
  | `output_format` | no | JSON Schema for structured output |
292
+ | `hooks` | no | Quality gates on prompt/command nodes: `pre:`/`post:` lists of `{run, name?, timeout?}` bash commands — `pre` failure blocks dispatch, `post` failure fails the node |
239
293
 
240
294
  ### Variables
241
295
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngocsangairvds/vsaf",
3
- "version": "5.1.17",
3
+ "version": "5.2.0",
4
4
  "description": "logging step",
5
5
  "main": "packages/core/dist/index.js",
6
6
  "types": "packages/core/dist/index.d.ts",
@@ -0,0 +1,6 @@
1
+ export interface AbandonResult {
2
+ runId: string;
3
+ status: 'cancelled';
4
+ }
5
+ export declare function execAbandon(runId: string, projectPath: string): Promise<AbandonResult>;
6
+ //# sourceMappingURL=abandon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abandon.d.ts","sourceRoot":"","sources":["../../src/commands/abandon.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAqC5F"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.execAbandon = execAbandon;
4
+ const path_1 = require("path");
5
+ const core_1 = require("@vsaf/core");
6
+ async function execAbandon(runId, projectPath) {
7
+ let found = false;
8
+ const sqlite = core_1.SqliteWorkflowStore.openProject(projectPath);
9
+ try {
10
+ const run = await sqlite.getWorkflowRun(runId);
11
+ if (run) {
12
+ if (run.status === 'completed') {
13
+ throw new Error(`Cannot abandon completed run: ${runId}`);
14
+ }
15
+ found = true;
16
+ await sqlite.updateWorkflowRun(runId, { status: 'cancelled' });
17
+ await sqlite.createWorkflowEvent({ workflow_run_id: runId, event_type: 'run:abandoned' });
18
+ await sqlite.addMessage(run.conversation_id, 'assistant', `Run ${runId} (${run.workflow_name}) was abandoned.`, {
19
+ category: 'workflow_status',
20
+ runId,
21
+ });
22
+ }
23
+ }
24
+ finally {
25
+ sqlite.close();
26
+ }
27
+ // Keep the legacy WAL store in sync so status/serve views agree.
28
+ const legacy = new core_1.RunStore((0, path_1.join)(projectPath, '.vsaf', 'storage'));
29
+ const run = legacy.load(runId);
30
+ if (run) {
31
+ if (!found && run.status === 'completed') {
32
+ throw new Error(`Cannot abandon completed run: ${runId}`);
33
+ }
34
+ legacy.cancel(runId);
35
+ found = true;
36
+ }
37
+ if (!found) {
38
+ throw new Error(`Run not found: ${runId}`);
39
+ }
40
+ return { runId, status: 'cancelled' };
41
+ }
42
+ //# sourceMappingURL=abandon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abandon.js","sourceRoot":"","sources":["../../src/commands/abandon.ts"],"names":[],"mappings":";;AAQA,kCAqCC;AA7CD,+BAA4B;AAC5B,qCAA2D;AAOpD,KAAK,UAAU,WAAW,CAAC,KAAa,EAAE,WAAmB;IAClE,IAAI,KAAK,GAAG,KAAK,CAAC;IAElB,MAAM,MAAM,GAAG,0BAAmB,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAC5D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CAAC,iCAAiC,KAAK,EAAE,CAAC,CAAC;YAC5D,CAAC;YACD,KAAK,GAAG,IAAI,CAAC;YACb,MAAM,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YAC/D,MAAM,MAAM,CAAC,mBAAmB,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,CAAC;YAC1F,MAAM,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,eAAe,EAAE,WAAW,EAAE,OAAO,KAAK,KAAK,GAAG,CAAC,aAAa,kBAAkB,EAAE;gBAC9G,QAAQ,EAAE,iBAAiB;gBAC3B,KAAK;aACN,CAAC,CAAC;QACL,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;IAED,iEAAiE;IACjE,MAAM,MAAM,GAAG,IAAI,eAAQ,CAAC,IAAA,WAAI,EAAC,WAAW,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;IACnE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,GAAG,EAAE,CAAC;QACR,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,iCAAiC,KAAK,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrB,KAAK,GAAG,IAAI,CAAC;IACf,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { type TierName } from '@vsaf/core';
2
+ export interface TierListEntry {
3
+ tier: TierName;
4
+ provider?: string;
5
+ model?: string;
6
+ source: 'config' | 'builtin' | 'unset';
7
+ }
8
+ export interface AliasListEntry {
9
+ name: string;
10
+ provider: string;
11
+ model: string;
12
+ }
13
+ export declare function execAiTierSet(tier: string, provider: string, model: string, homeDir?: string): void;
14
+ export declare function execAiTierUnset(tier: string, homeDir?: string): boolean;
15
+ export declare function execAiTierList(homeDir?: string, defaultProvider?: string): TierListEntry[];
16
+ export declare function execAiAliasSet(name: string, provider: string, model: string, homeDir?: string): void;
17
+ export declare function execAiAliasUnset(name: string, homeDir?: string): boolean;
18
+ export declare function execAiAliasList(homeDir?: string): AliasListEntry[];
19
+ export declare function formatTierList(entries: TierListEntry[]): string;
20
+ export declare function formatAliasList(entries: AliasListEntry[]): string;
21
+ //# sourceMappingURL=ai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/commands/ai.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,KAAK,QAAQ,EACd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAmB,GAAG,IAAI,CAG/G;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAmB,GAAG,OAAO,CAGnF;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,MAAmB,EAAE,eAAe,SAAW,GAAG,aAAa,EAAE,CAiBxG;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAmB,GAAG,IAAI,CAEhH;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAmB,GAAG,OAAO,CAEpF;AAED,wBAAgB,eAAe,CAAC,OAAO,GAAE,MAAmB,GAAG,cAAc,EAAE,CAK9E;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,CAW/D;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,MAAM,CASjE"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.execAiTierSet = execAiTierSet;
4
+ exports.execAiTierUnset = execAiTierUnset;
5
+ exports.execAiTierList = execAiTierList;
6
+ exports.execAiAliasSet = execAiAliasSet;
7
+ exports.execAiAliasUnset = execAiAliasUnset;
8
+ exports.execAiAliasList = execAiAliasList;
9
+ exports.formatTierList = formatTierList;
10
+ exports.formatAliasList = formatAliasList;
11
+ const core_1 = require("@vsaf/core");
12
+ function execAiTierSet(tier, provider, model, homeDir = (0, core_1.vsafHome)()) {
13
+ assertTier(tier);
14
+ (0, core_1.setTier)(tier, { provider, model }, homeDir);
15
+ }
16
+ function execAiTierUnset(tier, homeDir = (0, core_1.vsafHome)()) {
17
+ assertTier(tier);
18
+ return (0, core_1.unsetTier)(tier, homeDir);
19
+ }
20
+ function execAiTierList(homeDir = (0, core_1.vsafHome)(), defaultProvider = 'claude') {
21
+ const config = (0, core_1.loadAiConfig)(homeDir);
22
+ return core_1.TIER_NAMES.map((tier) => {
23
+ const configured = config.tiers[tier];
24
+ if (configured) {
25
+ return { tier, provider: configured.provider, model: configured.model, source: 'config' };
26
+ }
27
+ try {
28
+ // Only the tier's own built-in default (no sibling fallback) is a
29
+ // meaningful "builtin" row; resolveModelRef handles the lookup.
30
+ const preset = (0, core_1.resolveModelRef)(tier, defaultProvider, { tiers: {}, aliases: {} });
31
+ if (preset) {
32
+ return { tier, provider: preset.provider, model: preset.model, source: 'builtin' };
33
+ }
34
+ }
35
+ catch { /* no built-in default for this provider */ }
36
+ return { tier, source: 'unset' };
37
+ });
38
+ }
39
+ function execAiAliasSet(name, provider, model, homeDir = (0, core_1.vsafHome)()) {
40
+ (0, core_1.setAlias)(name, { provider, model }, homeDir);
41
+ }
42
+ function execAiAliasUnset(name, homeDir = (0, core_1.vsafHome)()) {
43
+ return (0, core_1.unsetAlias)(name, homeDir);
44
+ }
45
+ function execAiAliasList(homeDir = (0, core_1.vsafHome)()) {
46
+ const config = (0, core_1.loadAiConfig)(homeDir);
47
+ return Object.entries(config.aliases)
48
+ .map(([name, preset]) => ({ name, provider: preset.provider, model: preset.model }))
49
+ .sort((a, b) => a.name.localeCompare(b.name));
50
+ }
51
+ function formatTierList(entries) {
52
+ const lines = ['TIER'.padEnd(10) + 'PROVIDER'.padEnd(15) + 'MODEL'.padEnd(25) + 'SOURCE'];
53
+ for (const entry of entries) {
54
+ lines.push(entry.tier.padEnd(10)
55
+ + (entry.provider ?? '-').padEnd(15)
56
+ + (entry.model ?? '-').padEnd(25)
57
+ + entry.source);
58
+ }
59
+ return lines.join('\n');
60
+ }
61
+ function formatAliasList(entries) {
62
+ if (entries.length === 0) {
63
+ return 'No aliases defined. Run "vsaf ai alias set @name <provider> <model>" to add one.';
64
+ }
65
+ const lines = ['ALIAS'.padEnd(15) + 'PROVIDER'.padEnd(15) + 'MODEL'];
66
+ for (const entry of entries) {
67
+ lines.push(entry.name.padEnd(15) + entry.provider.padEnd(15) + entry.model);
68
+ }
69
+ return lines.join('\n');
70
+ }
71
+ function assertTier(tier) {
72
+ if (!(0, core_1.isTierName)(tier)) {
73
+ throw new Error(`Invalid tier '${tier}'. Valid tiers: ${core_1.TIER_NAMES.join(', ')}`);
74
+ }
75
+ }
76
+ //# sourceMappingURL=ai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/commands/ai.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export interface RejectResult {
2
+ runId: string;
3
+ status: 'cancelled';
4
+ reason?: string;
5
+ }
6
+ /**
7
+ * Approve a paused run and resume DAG execution from the paused node.
8
+ * Approval-node pauses complete the node (capturing the response when the
9
+ * node declares capture_response); interactive-loop pauses forward the
10
+ * response as loop feedback — the loop exits once the AI emits its signal.
11
+ */
12
+ export declare function execApprove(runId: string, projectPath: string, opts?: {
13
+ response?: string;
14
+ }): Promise<void>;
15
+ /** Reject a paused run: cancel it, keeping current reject semantics. */
16
+ export declare function execReject(runId: string, projectPath: string, opts?: {
17
+ reason?: string;
18
+ }): Promise<RejectResult>;
19
+ //# sourceMappingURL=approve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approve.d.ts","sourceRoot":"","sources":["../../src/commands/approve.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,wEAAwE;AACxE,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,OAAO,CAAC,YAAY,CAAC,CAiBvB"}
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
3
+ if (value !== null && value !== void 0) {
4
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
5
+ var dispose, inner;
6
+ if (async) {
7
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
8
+ dispose = value[Symbol.asyncDispose];
9
+ }
10
+ if (dispose === void 0) {
11
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
12
+ dispose = value[Symbol.dispose];
13
+ if (async) inner = dispose;
14
+ }
15
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
16
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
17
+ env.stack.push({ value: value, dispose: dispose, async: async });
18
+ }
19
+ else if (async) {
20
+ env.stack.push({ async: true });
21
+ }
22
+ return value;
23
+ };
24
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
25
+ return function (env) {
26
+ function fail(e) {
27
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
28
+ env.hasError = true;
29
+ }
30
+ var r, s = 0;
31
+ function next() {
32
+ while (r = env.stack.pop()) {
33
+ try {
34
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
35
+ if (r.dispose) {
36
+ var result = r.dispose.call(r.value);
37
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
38
+ }
39
+ else s |= 1;
40
+ }
41
+ catch (e) {
42
+ fail(e);
43
+ }
44
+ }
45
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
46
+ if (env.hasError) throw env.error;
47
+ }
48
+ return next();
49
+ };
50
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
51
+ var e = new Error(message);
52
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
53
+ });
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.execApprove = execApprove;
56
+ exports.execReject = execReject;
57
+ const path_1 = require("path");
58
+ const core_1 = require("@vsaf/core");
59
+ const run_js_1 = require("./run.js");
60
+ /**
61
+ * Approve a paused run and resume DAG execution from the paused node.
62
+ * Approval-node pauses complete the node (capturing the response when the
63
+ * node declares capture_response); interactive-loop pauses forward the
64
+ * response as loop feedback — the loop exits once the AI emits its signal.
65
+ */
66
+ async function execApprove(runId, projectPath, opts = {}) {
67
+ const run = loadPausedRun(runId, projectPath, 'approved');
68
+ if (run.pauseMetadata?.type === 'interactive_loop') {
69
+ await (0, run_js_1.execRunResume)(runId, projectPath, undefined, {}, { message: opts.response ?? 'approved' });
70
+ return;
71
+ }
72
+ await (0, run_js_1.execRunResume)(runId, projectPath, undefined, {}, {
73
+ approval: { approved: true, response: opts.response },
74
+ });
75
+ }
76
+ /** Reject a paused run: cancel it, keeping current reject semantics. */
77
+ async function execReject(runId, projectPath, opts = {}) {
78
+ const run = loadPausedRun(runId, projectPath, 'rejected');
79
+ const store = new core_1.RunStore((0, path_1.join)(projectPath, '.vsaf', 'storage'));
80
+ store.load(run.runId);
81
+ store.cancel(run.runId);
82
+ try {
83
+ const env_1 = { stack: [], error: void 0, hasError: false };
84
+ try {
85
+ const sqlite = __addDisposableResource(env_1, core_1.SqliteWorkflowStore.openProject(projectPath), false);
86
+ await sqlite.updateWorkflowRun(runId, { status: 'cancelled' });
87
+ await sqlite.createWorkflowEvent({
88
+ workflow_run_id: runId,
89
+ event_type: 'run:cancel',
90
+ data: { reason: opts.reason ?? 'rejected' },
91
+ });
92
+ }
93
+ catch (e_1) {
94
+ env_1.error = e_1;
95
+ env_1.hasError = true;
96
+ }
97
+ finally {
98
+ __disposeResources(env_1);
99
+ }
100
+ }
101
+ catch { /* SQLite handoff records are best-effort */ }
102
+ return { runId: run.runId, status: 'cancelled', reason: opts.reason };
103
+ }
104
+ function loadPausedRun(runId, projectPath, action) {
105
+ const store = new core_1.RunStore((0, path_1.join)(projectPath, '.vsaf', 'storage'));
106
+ const run = store.load(runId);
107
+ if (!run) {
108
+ throw new Error(`Run not found: ${runId}`);
109
+ }
110
+ if (run.status !== 'paused') {
111
+ throw new Error(`Only paused runs can be ${action} (run ${runId} is ${run.status})`);
112
+ }
113
+ return run;
114
+ }
115
+ //# sourceMappingURL=approve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approve.js","sourceRoot":"","sources":["../../src/commands/approve.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,kCAcC;AAGD,gCAqBC;AAtDD,+BAA4B;AAC5B,qCAA2D;AAC3D,qCAAyC;AAQzC;;;;;GAKG;AACI,KAAK,UAAU,WAAW,CAC/B,KAAa,EACb,WAAmB,EACnB,OAA8B,EAAE;IAEhC,MAAM,GAAG,GAAG,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;IAE1D,IAAI,GAAG,CAAC,aAAa,EAAE,IAAI,KAAK,kBAAkB,EAAE,CAAC;QACnD,MAAM,IAAA,sBAAa,EAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,IAAI,UAAU,EAAE,CAAC,CAAC;QACjG,OAAO;IACT,CAAC;IACD,MAAM,IAAA,sBAAa,EAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE;QACrD,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;KACtD,CAAC,CAAC;AACL,CAAC;AAED,wEAAwE;AACjE,KAAK,UAAU,UAAU,CAC9B,KAAa,EACb,WAAmB,EACnB,OAA4B,EAAE;IAE9B,MAAM,GAAG,GAAG,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,IAAI,eAAQ,CAAC,IAAA,WAAI,EAAC,WAAW,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;IAClE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAExB,IAAI,CAAC;;;YACH,MAAM,MAAM,kCAAG,0BAAmB,CAAC,WAAW,CAAC,WAAW,CAAC,QAAA,CAAC;YAC5D,MAAM,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YAC/D,MAAM,MAAM,CAAC,mBAAmB,CAAC;gBAC/B,eAAe,EAAE,KAAK;gBACtB,UAAU,EAAE,YAAY;gBACxB,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,UAAU,EAAE;aAC5C,CAAC,CAAC;;;;;;;;;KACJ;IAAC,MAAM,CAAC,CAAC,4CAA4C,CAAC,CAAC;IAExD,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AACxE,CAAC;AAED,SAAS,aAAa,CAAC,KAAa,EAAE,WAAmB,EAAE,MAAc;IACvE,MAAM,KAAK,GAAG,IAAI,eAAQ,CAAC,IAAA,WAAI,EAAC,WAAW,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,2BAA2B,MAAM,SAAS,KAAK,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { type ApprovalHandler } from './run.js';
2
+ export type ChatIntent = 'approve' | 'reject' | 'feedback';
3
+ export declare function parseChatIntent(message: string): ChatIntent;
4
+ /**
5
+ * Map a chat intent onto a gate decision. Feedback drives the revision loop
6
+ * (reject + injected error) when the gate can loop; otherwise it approves and
7
+ * the message flows downstream as the captured response.
8
+ */
9
+ export declare function decideGate(intent: ChatIntent, message: string, hasLoop: boolean): {
10
+ approved: boolean;
11
+ response: string;
12
+ };
13
+ /**
14
+ * Answers the first gate reached during a resume with the chat message and
15
+ * pauses again on any subsequent gate, so each chat turn consumes one gate.
16
+ */
17
+ export declare function chatApprovalHandler(intent: ChatIntent, message: string): ApprovalHandler;
18
+ export interface ChatOptions {
19
+ conversation?: string;
20
+ }
21
+ export interface PreparedChat {
22
+ conversationId: string;
23
+ action: 'resume' | 'run_active' | 'no_active_run';
24
+ runId?: string;
25
+ reply: string;
26
+ resume?: () => Promise<void>;
27
+ }
28
+ export interface ChatResult {
29
+ conversationId: string;
30
+ action: 'resumed' | 'run_active' | 'no_active_run';
31
+ runId?: string;
32
+ reply: string;
33
+ }
34
+ /**
35
+ * Simple conversation router (no orchestrator AI): pick the target
36
+ * conversation, persist the user message, and — when a paused run exists —
37
+ * prepare a resume that feeds the message into the gate.
38
+ */
39
+ export declare function prepareChat(projectPath: string, message: string, opts?: ChatOptions): Promise<PreparedChat>;
40
+ export declare function execChat(message: string, projectPath: string, opts?: ChatOptions): Promise<ChatResult>;
41
+ //# sourceMappingURL=chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,UAAU,CAAC;AAE/D,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;AAS3D,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAK3D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,GACf;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAMzC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,CAQxF;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,QAAQ,GAAG,YAAY,GAAG,eAAe,CAAC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,eAAe,CAAC;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAMD;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,YAAY,CAAC,CAyDvB;AAED,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,UAAU,CAAC,CAYrB"}
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseChatIntent = parseChatIntent;
4
+ exports.decideGate = decideGate;
5
+ exports.chatApprovalHandler = chatApprovalHandler;
6
+ exports.prepareChat = prepareChat;
7
+ exports.execChat = execChat;
8
+ const core_1 = require("@vsaf/core");
9
+ const run_js_1 = require("./run.js");
10
+ const APPROVE_WORDS = new Set([
11
+ 'y', 'yes', 'ok', 'okay', 'approve', 'approved', 'lgtm', 'proceed', 'continue', 'ship it',
12
+ ]);
13
+ const REJECT_WORDS = new Set([
14
+ 'n', 'no', 'reject', 'rejected', 'deny', 'denied', 'cancel', 'stop', 'abort',
15
+ ]);
16
+ function parseChatIntent(message) {
17
+ const normalized = message.trim().toLowerCase().replace(/[.!?]+$/, '');
18
+ if (APPROVE_WORDS.has(normalized))
19
+ return 'approve';
20
+ if (REJECT_WORDS.has(normalized))
21
+ return 'reject';
22
+ return 'feedback';
23
+ }
24
+ /**
25
+ * Map a chat intent onto a gate decision. Feedback drives the revision loop
26
+ * (reject + injected error) when the gate can loop; otherwise it approves and
27
+ * the message flows downstream as the captured response.
28
+ */
29
+ function decideGate(intent, message, hasLoop) {
30
+ if (intent === 'approve')
31
+ return { approved: true, response: message };
32
+ if (intent === 'reject')
33
+ return { approved: false, response: message };
34
+ return hasLoop
35
+ ? { approved: false, response: message }
36
+ : { approved: true, response: message };
37
+ }
38
+ /**
39
+ * Answers the first gate reached during a resume with the chat message and
40
+ * pauses again on any subsequent gate, so each chat turn consumes one gate.
41
+ */
42
+ function chatApprovalHandler(intent, message) {
43
+ let answered = false;
44
+ return async (gate) => {
45
+ if (answered)
46
+ return { action: 'pause' };
47
+ answered = true;
48
+ const decision = decideGate(intent, message, gate.hasLoop);
49
+ return { action: 'answer', ...decision };
50
+ };
51
+ }
52
+ function titleFrom(message) {
53
+ return message.length > 60 ? `${message.slice(0, 57)}...` : message;
54
+ }
55
+ /**
56
+ * Simple conversation router (no orchestrator AI): pick the target
57
+ * conversation, persist the user message, and — when a paused run exists —
58
+ * prepare a resume that feeds the message into the gate.
59
+ */
60
+ async function prepareChat(projectPath, message, opts = {}) {
61
+ if (message.trim().length === 0) {
62
+ throw new Error('Message must not be empty');
63
+ }
64
+ const store = core_1.SqliteWorkflowStore.openProject(projectPath);
65
+ try {
66
+ let conversationId;
67
+ let paused = null;
68
+ if (opts.conversation) {
69
+ conversationId = (await store.getOrCreateConversation(opts.conversation)).id;
70
+ paused = await store.getPausedWorkflowRun(conversationId);
71
+ }
72
+ else {
73
+ paused = await store.findLatestPausedRun();
74
+ if (paused) {
75
+ conversationId = paused.conversation_id;
76
+ }
77
+ else {
78
+ const recent = await store.listConversations(1);
79
+ conversationId = recent[0]?.id
80
+ ?? (await store.getOrCreateConversation(undefined, titleFrom(message))).id;
81
+ }
82
+ }
83
+ await store.addMessage(conversationId, 'user', message);
84
+ if (!paused) {
85
+ const active = await store.getActiveWorkflowRun(conversationId);
86
+ const reply = active
87
+ ? `Run ${active.id} (${active.workflow_name}) is ${active.status}; your message was saved to the conversation.`
88
+ : 'No paused run in this conversation. Start one with: vsaf run <workflow>';
89
+ await store.addMessage(conversationId, 'assistant', reply, {
90
+ category: 'workflow_status',
91
+ ...(active ? { runId: active.id } : {}),
92
+ });
93
+ return {
94
+ conversationId,
95
+ action: active ? 'run_active' : 'no_active_run',
96
+ runId: active?.id,
97
+ reply,
98
+ };
99
+ }
100
+ const intent = parseChatIntent(message);
101
+ const runId = paused.id;
102
+ const reply = `Resuming run ${runId} (${paused.workflow_name}) with your ${intent === 'feedback' ? 'feedback' : intent}.`;
103
+ await store.addMessage(conversationId, 'assistant', reply, { category: 'workflow_status', runId });
104
+ return {
105
+ conversationId,
106
+ action: 'resume',
107
+ runId,
108
+ reply,
109
+ resume: () => (0, run_js_1.execRunResume)(runId, projectPath, undefined, {}, { message }, chatApprovalHandler(intent, message)),
110
+ };
111
+ }
112
+ finally {
113
+ store.close();
114
+ }
115
+ }
116
+ async function execChat(message, projectPath, opts = {}) {
117
+ const prepared = await prepareChat(projectPath, message, opts);
118
+ if (prepared.action === 'resume' && prepared.resume) {
119
+ await prepared.resume();
120
+ return { conversationId: prepared.conversationId, action: 'resumed', runId: prepared.runId, reply: prepared.reply };
121
+ }
122
+ return {
123
+ conversationId: prepared.conversationId,
124
+ action: prepared.action,
125
+ runId: prepared.runId,
126
+ reply: prepared.reply,
127
+ };
128
+ }
129
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface IsolationEntry {
2
+ id: string;
3
+ path: string;
4
+ workflow?: string;
5
+ runStatus?: string;
6
+ branch?: string;
7
+ orphan: boolean;
8
+ }
9
+ export declare function isolationList(projectPath: string): Promise<IsolationEntry[]>;
10
+ export interface IsolationCleanupOptions {
11
+ merged?: boolean;
12
+ dryRun?: boolean;
13
+ }
14
+ export interface IsolationCleanupResult {
15
+ removed: IsolationEntry[];
16
+ kept: IsolationEntry[];
17
+ }
18
+ export declare function isolationCleanup(projectPath: string, opts?: IsolationCleanupOptions): Promise<IsolationCleanupResult>;
19
+ export declare function formatIsolationEntries(entries: IsolationEntry[]): string;
20
+ //# sourceMappingURL=isolation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isolation.d.ts","sourceRoot":"","sources":["../../src/commands/isolation.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAqBlF;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,IAAI,EAAE,cAAc,EAAE,CAAC;CACxB;AAED,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,uBAA4B,GACjC,OAAO,CAAC,sBAAsB,CAAC,CA0BjC;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,MAAM,CAOxE"}