@flowdesk/opencode-plugin 0.1.11 → 0.1.13

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 (72) hide show
  1. package/dist/agent-task-runner.d.ts +27 -0
  2. package/dist/agent-task-runner.d.ts.map +1 -0
  3. package/dist/agent-task-runner.js +390 -0
  4. package/dist/agent-task-runner.js.map +1 -0
  5. package/dist/bootstrap-installer.d.ts +3 -0
  6. package/dist/bootstrap-installer.d.ts.map +1 -1
  7. package/dist/bootstrap-installer.js +153 -7
  8. package/dist/bootstrap-installer.js.map +1 -1
  9. package/dist/command-handlers.d.ts +3 -0
  10. package/dist/command-handlers.d.ts.map +1 -1
  11. package/dist/command-handlers.js +38 -4
  12. package/dist/command-handlers.js.map +1 -1
  13. package/dist/controlled-write-tool.d.ts +49 -0
  14. package/dist/controlled-write-tool.d.ts.map +1 -0
  15. package/dist/controlled-write-tool.js +296 -0
  16. package/dist/controlled-write-tool.js.map +1 -0
  17. package/dist/local-adapter.d.ts.map +1 -1
  18. package/dist/local-adapter.js +19 -0
  19. package/dist/local-adapter.js.map +1 -1
  20. package/dist/managed-dispatch-adapter.d.ts +3 -0
  21. package/dist/managed-dispatch-adapter.d.ts.map +1 -1
  22. package/dist/managed-dispatch-adapter.js +179 -27
  23. package/dist/managed-dispatch-adapter.js.map +1 -1
  24. package/dist/provider-usage-live-tool.d.ts +17 -0
  25. package/dist/provider-usage-live-tool.d.ts.map +1 -1
  26. package/dist/provider-usage-live-tool.js +317 -5
  27. package/dist/provider-usage-live-tool.js.map +1 -1
  28. package/dist/quick-reviewer-run.d.ts +16 -2
  29. package/dist/quick-reviewer-run.d.ts.map +1 -1
  30. package/dist/quick-reviewer-run.js +228 -72
  31. package/dist/quick-reviewer-run.js.map +1 -1
  32. package/dist/runtime-reviewer-execution-bridge.d.ts +21 -0
  33. package/dist/runtime-reviewer-execution-bridge.d.ts.map +1 -1
  34. package/dist/runtime-reviewer-execution-bridge.js +284 -1
  35. package/dist/runtime-reviewer-execution-bridge.js.map +1 -1
  36. package/dist/server.d.ts +72 -1
  37. package/dist/server.d.ts.map +1 -1
  38. package/dist/server.js +816 -77
  39. package/dist/server.js.map +1 -1
  40. package/dist/shared/with-timeout.d.ts +12 -0
  41. package/dist/shared/with-timeout.d.ts.map +1 -0
  42. package/dist/shared/with-timeout.js +31 -0
  43. package/dist/shared/with-timeout.js.map +1 -0
  44. package/dist/stall-recovery.d.ts +214 -0
  45. package/dist/stall-recovery.d.ts.map +1 -0
  46. package/dist/stall-recovery.js +1257 -0
  47. package/dist/stall-recovery.js.map +1 -0
  48. package/dist/status-live-tool.d.ts +28 -0
  49. package/dist/status-live-tool.d.ts.map +1 -1
  50. package/dist/status-live-tool.js +306 -1
  51. package/dist/status-live-tool.js.map +1 -1
  52. package/dist/tui-usage-snapshot.d.ts +30 -0
  53. package/dist/tui-usage-snapshot.d.ts.map +1 -0
  54. package/dist/tui-usage-snapshot.js +216 -0
  55. package/dist/tui-usage-snapshot.js.map +1 -0
  56. package/dist/tui.d.ts +7 -0
  57. package/dist/tui.d.ts.map +1 -0
  58. package/dist/tui.js +103 -0
  59. package/dist/tui.js.map +1 -0
  60. package/dist/workflow-dispatch-plan-tool.d.ts +47 -0
  61. package/dist/workflow-dispatch-plan-tool.d.ts.map +1 -0
  62. package/dist/workflow-dispatch-plan-tool.js +251 -0
  63. package/dist/workflow-dispatch-plan-tool.js.map +1 -0
  64. package/dist/workflow-dispatch-tool.d.ts +56 -0
  65. package/dist/workflow-dispatch-tool.d.ts.map +1 -0
  66. package/dist/workflow-dispatch-tool.js +276 -0
  67. package/dist/workflow-dispatch-tool.js.map +1 -0
  68. package/dist/workflow-scheduler.d.ts +19 -0
  69. package/dist/workflow-scheduler.d.ts.map +1 -0
  70. package/dist/workflow-scheduler.js +43 -0
  71. package/dist/workflow-scheduler.js.map +1 -0
  72. package/package.json +10 -2
@@ -0,0 +1,216 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ const providerFamilies = [
4
+ "claude",
5
+ "openai",
6
+ "gemini",
7
+ ];
8
+ function isRecord(value) {
9
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10
+ }
11
+ function isProviderFamily(value) {
12
+ return typeof value === "string" && providerFamilies.includes(value);
13
+ }
14
+ function safeRootDir(value) {
15
+ if (typeof value === "string" && value.trim().length > 0)
16
+ return value;
17
+ const home = process.env.HOME;
18
+ return typeof home === "string" && home.length > 0 ? join(home, ".flowdesk") : ".flowdesk";
19
+ }
20
+ function safeWorkflowId(value) {
21
+ return typeof value === "string" && /^[A-Za-z0-9_-]+$/.test(value)
22
+ ? value
23
+ : "workflow-provider-usage-live";
24
+ }
25
+ function timestampFromUsageSnapshotId(value) {
26
+ if (typeof value !== "string")
27
+ return undefined;
28
+ const match = /(\d{8}T\d{9}Z)$/.exec(value);
29
+ if (match === null)
30
+ return undefined;
31
+ const stamp = match[1];
32
+ const iso = `${stamp.slice(0, 4)}-${stamp.slice(4, 6)}-${stamp.slice(6, 8)}T${stamp.slice(9, 11)}:${stamp.slice(11, 13)}:${stamp.slice(13, 15)}.${stamp.slice(15, 18)}Z`;
33
+ const parsed = Date.parse(iso);
34
+ return Number.isFinite(parsed) ? parsed : undefined;
35
+ }
36
+ function remainingPercentFromResetBucket(value) {
37
+ if (typeof value !== "string")
38
+ return null;
39
+ const match = /^([0-9]+(?:\.[0-9]+)?)%/.exec(value);
40
+ if (match === null)
41
+ return null;
42
+ const parsed = Number.parseFloat(match[1]);
43
+ return Number.isFinite(parsed) ? parsed : null;
44
+ }
45
+ function alertLevelFor(freshness, remainingPercent) {
46
+ if (freshness !== "fresh")
47
+ return freshness === "stale" ? "stale" : "unknown";
48
+ if (remainingPercent === null)
49
+ return "unknown";
50
+ if (remainingPercent <= 0)
51
+ return "exhausted";
52
+ if (remainingPercent <= 10)
53
+ return "critical";
54
+ if (remainingPercent <= 30)
55
+ return "warning";
56
+ return "ok";
57
+ }
58
+ function rowFromRecord(family, record, nowMs) {
59
+ if (record === undefined) {
60
+ return {
61
+ providerFamily: family,
62
+ connected: false,
63
+ dispatchability: "non_dispatchable",
64
+ freshness: "unknown",
65
+ remainingPercent: null,
66
+ alertLevel: "unknown",
67
+ };
68
+ }
69
+ const dispatchability = record.dispatchability === "dispatchable" ||
70
+ record.dispatchability === "diagnostic_only" ||
71
+ record.dispatchability === "non_dispatchable"
72
+ ? record.dispatchability
73
+ : "non_dispatchable";
74
+ const freshness = record.freshness === "fresh" || record.freshness === "stale" || record.freshness === "unknown"
75
+ ? record.freshness
76
+ : "unknown";
77
+ const resetTime = typeof record.reset_time === "string" ? record.reset_time : undefined;
78
+ const resetMs = resetTime === undefined ? Number.NaN : Date.parse(resetTime);
79
+ const observedMs = timestampFromUsageSnapshotId(record.snapshot_id);
80
+ const remainingPercent = remainingPercentFromResetBucket(record.reset_bucket);
81
+ return {
82
+ providerFamily: family,
83
+ connected: dispatchability === "dispatchable" && freshness === "fresh",
84
+ dispatchability,
85
+ freshness,
86
+ ...(typeof record.reset_bucket === "string" ? { resetBucket: record.reset_bucket } : {}),
87
+ ...(resetTime === undefined ? {} : { resetTime }),
88
+ remainingPercent,
89
+ alertLevel: alertLevelFor(freshness, remainingPercent),
90
+ ...(typeof record.snapshot_id === "string" ? { usageSnapshotRef: record.snapshot_id } : {}),
91
+ ...(Number.isFinite(resetMs)
92
+ ? { secondsUntilReset: Math.max(0, Math.floor((resetMs - nowMs) / 1000)) }
93
+ : {}),
94
+ ...(observedMs === undefined
95
+ ? {}
96
+ : { secondsSinceObserved: Math.max(0, Math.floor((nowMs - observedMs) / 1000)) }),
97
+ };
98
+ }
99
+ function rowFromSidebarCacheRecord(family, record, nowMs) {
100
+ if (record === undefined)
101
+ return rowFromRecord(family, undefined, nowMs);
102
+ const dispatchability = record.dispatchability === "dispatchable" ||
103
+ record.dispatchability === "diagnostic_only" ||
104
+ record.dispatchability === "non_dispatchable"
105
+ ? record.dispatchability
106
+ : "non_dispatchable";
107
+ const freshness = record.freshness === "fresh" || record.freshness === "stale" || record.freshness === "unknown"
108
+ ? record.freshness
109
+ : "unknown";
110
+ const alertLevel = record.alertLevel === "ok" ||
111
+ record.alertLevel === "warning" ||
112
+ record.alertLevel === "critical" ||
113
+ record.alertLevel === "exhausted" ||
114
+ record.alertLevel === "stale" ||
115
+ record.alertLevel === "unknown"
116
+ ? record.alertLevel
117
+ : "unknown";
118
+ const remainingPercent = typeof record.remainingPercent === "number" && Number.isFinite(record.remainingPercent)
119
+ ? record.remainingPercent
120
+ : null;
121
+ const resetTime = typeof record.resetTime === "string" ? record.resetTime : undefined;
122
+ const resetMs = resetTime === undefined ? Number.NaN : Date.parse(resetTime);
123
+ return {
124
+ providerFamily: family,
125
+ connected: record.connected === true,
126
+ dispatchability,
127
+ freshness,
128
+ ...(typeof record.resetBucket === "string" ? { resetBucket: record.resetBucket } : {}),
129
+ ...(resetTime === undefined ? {} : { resetTime }),
130
+ remainingPercent,
131
+ alertLevel,
132
+ ...(typeof record.usageSnapshotRef === "string" ? { usageSnapshotRef: record.usageSnapshotRef } : {}),
133
+ ...(Number.isFinite(resetMs)
134
+ ? { secondsUntilReset: Math.max(0, Math.floor((resetMs - nowMs) / 1000)) }
135
+ : {}),
136
+ };
137
+ }
138
+ function loadSidebarCacheRows(rootDir, nowMs) {
139
+ try {
140
+ const cache = JSON.parse(readFileSync(join(rootDir, ".flowdesk", "ui", "provider-usage-sidebar.json"), "utf8"));
141
+ if (!isRecord(cache) || cache.schema_version !== "flowdesk.provider_usage_sidebar_cache.v1")
142
+ return undefined;
143
+ if (!Array.isArray(cache.providers))
144
+ return undefined;
145
+ const byFamily = new Map();
146
+ for (const row of cache.providers) {
147
+ if (!isRecord(row) || !isProviderFamily(row.providerFamily))
148
+ continue;
149
+ byFamily.set(row.providerFamily, row);
150
+ }
151
+ if (byFamily.size === 0)
152
+ return undefined;
153
+ return providerFamilies.map((family) => rowFromSidebarCacheRecord(family, byFamily.get(family), nowMs));
154
+ }
155
+ catch {
156
+ return undefined;
157
+ }
158
+ }
159
+ export function loadFlowDeskTuiUsageSnapshotViewV1(input = {}) {
160
+ const observedAt = (input.now ? input.now() : new Date()).toISOString();
161
+ const nowMs = Date.parse(observedAt);
162
+ const rootDir = safeRootDir(input.rootDir);
163
+ const workflowId = safeWorkflowId(input.workflowId);
164
+ const sidebarRows = loadSidebarCacheRows(rootDir, nowMs);
165
+ if (sidebarRows !== undefined) {
166
+ return {
167
+ status: "loaded",
168
+ observedAt,
169
+ rootDir,
170
+ workflowId,
171
+ providers: sidebarRows,
172
+ safeNextActions: ["/flowdesk-usage", "/flowdesk-status", "/flowdesk-doctor"],
173
+ };
174
+ }
175
+ const evidenceDir = join(rootDir, ".flowdesk", "sessions", workflowId, "evidence", "provider-usage-snapshot");
176
+ try {
177
+ const byFamily = new Map();
178
+ for (const name of readdirSync(evidenceDir)) {
179
+ if (!name.endsWith(".json"))
180
+ continue;
181
+ const record = JSON.parse(readFileSync(join(evidenceDir, name), "utf8"));
182
+ if (!isRecord(record) || record.schema_version !== "flowdesk.usage_snapshot.v1")
183
+ continue;
184
+ if (!isProviderFamily(record.provider_family))
185
+ continue;
186
+ const previous = byFamily.get(record.provider_family);
187
+ const previousMs = timestampFromUsageSnapshotId(previous?.snapshot_id);
188
+ const currentMs = timestampFromUsageSnapshotId(record.snapshot_id);
189
+ if (previous === undefined || (currentMs ?? 0) >= (previousMs ?? 0)) {
190
+ byFamily.set(record.provider_family, record);
191
+ }
192
+ }
193
+ const providers = providerFamilies.map((family) => rowFromRecord(family, byFamily.get(family), nowMs));
194
+ return {
195
+ status: byFamily.size === 0 ? "missing" : "loaded",
196
+ observedAt,
197
+ rootDir,
198
+ workflowId,
199
+ providers,
200
+ ...(byFamily.size === 0 ? { redactedReason: "no provider usage snapshots found" } : {}),
201
+ safeNextActions: ["/flowdesk-usage", "/flowdesk-status", "/flowdesk-doctor"],
202
+ };
203
+ }
204
+ catch {
205
+ return {
206
+ status: "blocked",
207
+ observedAt,
208
+ rootDir,
209
+ workflowId,
210
+ providers: providerFamilies.map((family) => rowFromRecord(family, undefined, nowMs)),
211
+ redactedReason: "provider usage snapshot cache is unavailable",
212
+ safeNextActions: ["/flowdesk-usage", "/flowdesk-status", "/flowdesk-doctor"],
213
+ };
214
+ }
215
+ }
216
+ //# sourceMappingURL=tui-usage-snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui-usage-snapshot.js","sourceRoot":"","sources":["../src/tui-usage-snapshot.ts"],"names":[],"mappings":"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"}
package/dist/tui.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { TuiPlugin } from "@opencode-ai/plugin/tui";
2
+ declare const _default: {
3
+ id: string;
4
+ tui: TuiPlugin;
5
+ };
6
+ export default _default;
7
+ //# sourceMappingURL=tui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,yBAAyB,CAAC;;;;;AAsH1E,wBAG6C"}
package/dist/tui.js ADDED
@@ -0,0 +1,103 @@
1
+ import { createElement, createTextNode, insert, setProp } from "@opentui/solid";
2
+ import { loadFlowDeskTuiUsageSnapshotViewV1 } from "./tui-usage-snapshot.js";
3
+ function optionsFrom(value) {
4
+ if (typeof value !== "object" || value === null || Array.isArray(value))
5
+ return {};
6
+ const record = value;
7
+ return {
8
+ ...(typeof record.durableStateRootDir === "string"
9
+ ? { durableStateRootDir: record.durableStateRootDir }
10
+ : {}),
11
+ ...(typeof record.usageWorkflowId === "string" ? { usageWorkflowId: record.usageWorkflowId } : {}),
12
+ ...(typeof record.showAppBottom === "boolean" ? { showAppBottom: record.showAppBottom } : {}),
13
+ ...(typeof record.showSessionPromptRight === "boolean"
14
+ ? { showSessionPromptRight: record.showSessionPromptRight }
15
+ : {}),
16
+ };
17
+ }
18
+ function textLine(value) {
19
+ const node = createElement("text");
20
+ insert(node, createTextNode(value));
21
+ return node;
22
+ }
23
+ function box(children, props = {}) {
24
+ const node = createElement("box");
25
+ for (const [key, value] of Object.entries(props))
26
+ setProp(node, key, value);
27
+ for (const child of children)
28
+ insert(node, child);
29
+ return node;
30
+ }
31
+ function percentLabel(value) {
32
+ return value === null ? "?" : `${Math.round(value)}%`;
33
+ }
34
+ function resetLabel(seconds) {
35
+ if (seconds === undefined)
36
+ return "reset ?";
37
+ if (seconds < 60)
38
+ return "reset <1m";
39
+ if (seconds < 3600)
40
+ return `reset ${Math.ceil(seconds / 60)}m`;
41
+ return `reset ${Math.ceil(seconds / 3600)}h`;
42
+ }
43
+ const providerLabels = {
44
+ claude: "Claude",
45
+ openai: "OpenAI",
46
+ gemini: "Gemini",
47
+ };
48
+ const statusGlyph = {
49
+ ok: "●",
50
+ warning: "▲",
51
+ critical: "!",
52
+ exhausted: "×",
53
+ stale: "◌",
54
+ unknown: "?",
55
+ };
56
+ function usageSidebar(options) {
57
+ const view = loadFlowDeskTuiUsageSnapshotViewV1({
58
+ rootDir: options.durableStateRootDir,
59
+ workflowId: options.usageWorkflowId,
60
+ });
61
+ return box([
62
+ textLine("FlowDesk subscriptions"),
63
+ ...view.providers.map((provider) => textLine(`${statusGlyph[provider.alertLevel]} ${providerLabels[provider.providerFamily]} · ${provider.connected ? "connected" : "unavailable"} · ${percentLabel(provider.remainingPercent)} · ${resetLabel(provider.secondsUntilReset)}`)),
64
+ textLine(view.status === "loaded" ? "cache readable" : "run /flowdesk-usage"),
65
+ ], { flexShrink: 0, gap: 1, paddingTop: 1, paddingBottom: 1 });
66
+ }
67
+ function usageBadge(options) {
68
+ const view = loadFlowDeskTuiUsageSnapshotViewV1({
69
+ rootDir: options.durableStateRootDir,
70
+ workflowId: options.usageWorkflowId,
71
+ });
72
+ const connected = view.providers.filter((provider) => provider.connected).length;
73
+ return textLine(`FD ${connected}/${view.providers.length}`);
74
+ }
75
+ const tui = async (api, rawOptions) => {
76
+ const options = optionsFrom(rawOptions);
77
+ api.slots.register({
78
+ slots: {
79
+ sidebar_content() {
80
+ return usageSidebar(options);
81
+ },
82
+ ...(options.showAppBottom === true
83
+ ? {
84
+ app_bottom() {
85
+ return usageBadge(options);
86
+ },
87
+ }
88
+ : {}),
89
+ ...(options.showSessionPromptRight === true
90
+ ? {
91
+ session_prompt_right() {
92
+ return usageBadge(options);
93
+ },
94
+ }
95
+ : {}),
96
+ },
97
+ });
98
+ };
99
+ export default {
100
+ id: "flowdesk.tui",
101
+ tui,
102
+ };
103
+ //# sourceMappingURL=tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ export interface FlowDeskWorkflowDispatchPlanToolConfigV1 {
2
+ rootDir: string;
3
+ }
4
+ export interface FlowDeskWorkflowDispatchPlanToolTaskRequestV1 {
5
+ agentRole?: string;
6
+ title?: string;
7
+ summary?: string;
8
+ agentRoleRef?: string;
9
+ dependsOnTaskIds?: string[];
10
+ }
11
+ export interface FlowDeskWorkflowDispatchPlanToolRequestV1 {
12
+ workflowId?: string;
13
+ goalSummary?: string;
14
+ selectedAgentRoles?: string[];
15
+ tasks?: FlowDeskWorkflowDispatchPlanToolTaskRequestV1[];
16
+ }
17
+ export interface FlowDeskWorkflowDispatchPlanToolResultV1 {
18
+ status: "workflow_dispatch_plan_recorded" | "blocked_before_workflow_dispatch_plan";
19
+ rootDir?: string;
20
+ workflowId?: string;
21
+ planRevisionId?: string;
22
+ selectedAgentRoleCount: number;
23
+ taskCount: number;
24
+ writeAttempted: boolean;
25
+ evidenceReloaded: boolean;
26
+ summaryForUser: string;
27
+ redactedBlockReason?: string;
28
+ safeNextActions: readonly ("/flowdesk-status" | "/flowdesk-plan" | "/flowdesk-doctor")[];
29
+ authority: {
30
+ realOpenCodeDispatch: false;
31
+ providerCall: false;
32
+ runtimeExecution: false;
33
+ actualLaneLaunch: false;
34
+ fallbackAuthority: false;
35
+ hardCancelOrNoReplyAuthority: false;
36
+ toolAuthority: false;
37
+ dispatchAuthorityEnabled: false;
38
+ workflowDispatchPlanPersisted: boolean;
39
+ };
40
+ }
41
+ export declare function executeFlowDeskWorkflowDispatchPlanToolV1(input: {
42
+ config: FlowDeskWorkflowDispatchPlanToolConfigV1;
43
+ request?: FlowDeskWorkflowDispatchPlanToolRequestV1;
44
+ rawInput?: unknown;
45
+ now?: () => Date;
46
+ }): FlowDeskWorkflowDispatchPlanToolResultV1;
47
+ //# sourceMappingURL=workflow-dispatch-plan-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-dispatch-plan-tool.d.ts","sourceRoot":"","sources":["../src/workflow-dispatch-plan-tool.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,wCAAwC;IACxD,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,6CAA6C;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,yCAAyC;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,6CAA6C,EAAE,CAAC;CACxD;AAED,MAAM,WAAW,wCAAwC;IACxD,MAAM,EACH,iCAAiC,GACjC,uCAAuC,CAAC;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,SAAS,CAAC,kBAAkB,GAAG,gBAAgB,GAAG,kBAAkB,CAAC,EAAE,CAAC;IACzF,SAAS,EAAE;QACV,oBAAoB,EAAE,KAAK,CAAC;QAC5B,YAAY,EAAE,KAAK,CAAC;QACpB,gBAAgB,EAAE,KAAK,CAAC;QACxB,gBAAgB,EAAE,KAAK,CAAC;QACxB,iBAAiB,EAAE,KAAK,CAAC;QACzB,4BAA4B,EAAE,KAAK,CAAC;QACpC,aAAa,EAAE,KAAK,CAAC;QACrB,wBAAwB,EAAE,KAAK,CAAC;QAChC,6BAA6B,EAAE,OAAO,CAAC;KACvC,CAAC;CACF;AA0HD,wBAAgB,yCAAyC,CAAC,KAAK,EAAE;IAChE,MAAM,EAAE,wCAAwC,CAAC;IACjD,OAAO,CAAC,EAAE,yCAAyC,CAAC;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CACjB,GAAG,wCAAwC,CA8J3C"}
@@ -0,0 +1,251 @@
1
+ import { applyFlowDeskSessionEvidenceWriteIntentsV1, evaluateFlowDeskWorkflowDispatchPlanningV1, prepareFlowDeskSessionEvidenceWriteIntentV1, reloadFlowDeskSessionEvidenceV1, } from "@flowdesk/core";
2
+ const authoritySmugglingKeys = new Set([
3
+ "allowProviderCall",
4
+ "actualLaneLaunch",
5
+ "dispatchAuthorityEnabled",
6
+ "fallbackAuthority",
7
+ "providerCall",
8
+ "realOpenCodeDispatch",
9
+ "runtimeExecution",
10
+ "toolAuthority",
11
+ ]);
12
+ function authority(persisted) {
13
+ return {
14
+ realOpenCodeDispatch: false,
15
+ providerCall: false,
16
+ runtimeExecution: false,
17
+ actualLaneLaunch: false,
18
+ fallbackAuthority: false,
19
+ hardCancelOrNoReplyAuthority: false,
20
+ toolAuthority: false,
21
+ dispatchAuthorityEnabled: false,
22
+ workflowDispatchPlanPersisted: persisted,
23
+ };
24
+ }
25
+ function safeNextActions() {
26
+ return ["/flowdesk-status", "/flowdesk-plan", "/flowdesk-doctor"];
27
+ }
28
+ function blocked(input) {
29
+ const workflowId = input.workflowId ?? input.request?.workflowId;
30
+ return {
31
+ status: "blocked_before_workflow_dispatch_plan",
32
+ rootDir: input.config.rootDir,
33
+ ...(workflowId === undefined ? {} : { workflowId }),
34
+ ...(input.planRevisionId === undefined
35
+ ? {}
36
+ : { planRevisionId: input.planRevisionId }),
37
+ selectedAgentRoleCount: input.roleCount ?? 0,
38
+ taskCount: input.taskCount ?? 0,
39
+ writeAttempted: false,
40
+ evidenceReloaded: false,
41
+ summaryForUser: `FlowDesk workflow dispatch planning blocked: ${input.reason}`,
42
+ redactedBlockReason: input.reason,
43
+ safeNextActions: safeNextActions(),
44
+ authority: authority(false),
45
+ };
46
+ }
47
+ function isRecord(value) {
48
+ return typeof value === "object" && value !== null && !Array.isArray(value);
49
+ }
50
+ function stableToken(value, fallback) {
51
+ const token = value.replaceAll(/[^A-Za-z0-9_.:-]/g, "-").slice(0, 96);
52
+ return token.length >= 3 ? token : fallback;
53
+ }
54
+ function hasAuthoritySmuggling(value) {
55
+ if (!isRecord(value))
56
+ return false;
57
+ for (const [key, entry] of Object.entries(value)) {
58
+ if (authoritySmugglingKeys.has(key) && entry !== false && entry !== undefined)
59
+ return true;
60
+ if (isRecord(entry) && hasAuthoritySmuggling(entry))
61
+ return true;
62
+ if (Array.isArray(entry) && entry.some((item) => hasAuthoritySmuggling(item)))
63
+ return true;
64
+ }
65
+ return false;
66
+ }
67
+ function normalizeRequest(value) {
68
+ if (!isRecord(value))
69
+ return {};
70
+ return {
71
+ ...(typeof value.workflowId === "string"
72
+ ? { workflowId: value.workflowId }
73
+ : {}),
74
+ ...(typeof value.goalSummary === "string"
75
+ ? { goalSummary: value.goalSummary }
76
+ : {}),
77
+ ...(Array.isArray(value.selectedAgentRoles)
78
+ ? {
79
+ selectedAgentRoles: value.selectedAgentRoles.filter((role) => typeof role === "string"),
80
+ }
81
+ : {}),
82
+ ...(Array.isArray(value.tasks)
83
+ ? {
84
+ tasks: value.tasks.filter(isRecord).map((task) => ({
85
+ ...(typeof task.agentRole === "string"
86
+ ? { agentRole: task.agentRole }
87
+ : {}),
88
+ ...(typeof task.title === "string" ? { title: task.title } : {}),
89
+ ...(typeof task.summary === "string"
90
+ ? { summary: task.summary }
91
+ : {}),
92
+ ...(typeof task.agentRoleRef === "string"
93
+ ? { agentRoleRef: task.agentRoleRef }
94
+ : {}),
95
+ ...(Array.isArray(task.dependsOnTaskIds)
96
+ ? {
97
+ dependsOnTaskIds: task.dependsOnTaskIds.filter((id) => typeof id === "string"),
98
+ }
99
+ : {}),
100
+ })),
101
+ }
102
+ : {}),
103
+ };
104
+ }
105
+ export function executeFlowDeskWorkflowDispatchPlanToolV1(input) {
106
+ const request = input.request ?? normalizeRequest(input.rawInput);
107
+ if (typeof input.config.rootDir !== "string" || input.config.rootDir.trim().length === 0)
108
+ return blocked({
109
+ config: input.config,
110
+ request,
111
+ reason: "workflowDispatchPlanTool requires a durable state root directory",
112
+ });
113
+ if (hasAuthoritySmuggling(input.rawInput ?? request))
114
+ return blocked({
115
+ config: input.config,
116
+ request,
117
+ reason: "request contains dispatch/provider/runtime authority fields",
118
+ });
119
+ if (typeof request.goalSummary !== "string" || request.goalSummary.trim().length === 0)
120
+ return blocked({
121
+ config: input.config,
122
+ request,
123
+ reason: "goalSummary is required",
124
+ });
125
+ if (!Array.isArray(request.tasks) || request.tasks.length === 0)
126
+ return blocked({
127
+ config: input.config,
128
+ request,
129
+ reason: "at least one task is required",
130
+ });
131
+ const observed = input.now ? input.now() : new Date();
132
+ const stamp = observed.toISOString().replaceAll(/[-:.]/g, "").replace("Z", "Z");
133
+ const workflowId = stableToken(request.workflowId ?? `workflow-dispatch-plan-${stamp}`, `workflow-dispatch-plan-${stamp}`);
134
+ const planRevisionId = stableToken(`workflow-dispatch-plan-${workflowId}-${stamp}`, `workflow-dispatch-plan-${stamp}`);
135
+ const tasks = request.tasks.map((task, index) => ({
136
+ task_id: stableToken(`task-${index + 1}-${workflowId}`, `task-${index + 1}`),
137
+ title: task.title ?? `Task ${index + 1}`,
138
+ summary: task.summary ?? "FlowDesk planning task summary not provided.",
139
+ agent_role: (task.agentRole ?? "implementation"),
140
+ ...(task.agentRoleRef === undefined ? {} : { agent_role_ref: task.agentRoleRef }),
141
+ ...(task.dependsOnTaskIds === undefined
142
+ ? {}
143
+ : { depends_on_task_ids: task.dependsOnTaskIds }),
144
+ }));
145
+ const selectedRoles = new Set([
146
+ ...(request.selectedAgentRoles ?? []),
147
+ ...tasks.map((task) => task.agent_role),
148
+ ]);
149
+ const selectedAgentRoles = [
150
+ ...selectedRoles,
151
+ ].map((role) => ({ agent_role: role }));
152
+ const evaluation = evaluateFlowDeskWorkflowDispatchPlanningV1({
153
+ workflowId,
154
+ planRevisionId,
155
+ requestedGoalSummary: request.goalSummary,
156
+ selectedAgentRoles,
157
+ tasks,
158
+ taskGraphSummary: `Planning-only workflow graph with ${tasks.length} task(s).`,
159
+ modelSelectionDiagnosticLabels: ["planning_only_no_provider_call"],
160
+ });
161
+ if (!evaluation.ok || evaluation.plan === undefined)
162
+ return blocked({
163
+ config: input.config,
164
+ request,
165
+ reason: evaluation.errors.join(", ") || "workflow dispatch plan validation failed",
166
+ workflowId,
167
+ planRevisionId,
168
+ roleCount: selectedAgentRoles.length,
169
+ taskCount: tasks.length,
170
+ });
171
+ const reload = reloadFlowDeskSessionEvidenceV1({ workflowId, rootDir: input.config.rootDir });
172
+ if (!reload.ok || reload.blocked.length > 0)
173
+ return blocked({
174
+ config: input.config,
175
+ request,
176
+ reason: "session evidence reload failed before workflow dispatch plan write",
177
+ workflowId,
178
+ planRevisionId,
179
+ roleCount: selectedAgentRoles.length,
180
+ taskCount: tasks.length,
181
+ });
182
+ if (reload.entries.some((entry) => entry.evidenceId === planRevisionId))
183
+ return blocked({
184
+ config: input.config,
185
+ request,
186
+ reason: "workflow dispatch plan evidence id already exists",
187
+ workflowId,
188
+ planRevisionId,
189
+ roleCount: selectedAgentRoles.length,
190
+ taskCount: tasks.length,
191
+ });
192
+ const prepared = prepareFlowDeskSessionEvidenceWriteIntentV1({
193
+ workflowId,
194
+ evidenceId: planRevisionId,
195
+ record: evaluation.plan,
196
+ });
197
+ if (!prepared.ok || prepared.writeIntent === undefined)
198
+ return blocked({
199
+ config: input.config,
200
+ request,
201
+ reason: prepared.errors.join(", ") || "workflow dispatch plan write intent preparation failed",
202
+ workflowId,
203
+ planRevisionId,
204
+ roleCount: selectedAgentRoles.length,
205
+ taskCount: tasks.length,
206
+ });
207
+ const applied = applyFlowDeskSessionEvidenceWriteIntentsV1(input.config.rootDir, [
208
+ prepared.writeIntent,
209
+ ]);
210
+ if (!applied.ok)
211
+ return blocked({
212
+ config: input.config,
213
+ request,
214
+ reason: applied.errors.join(", ") || "workflow dispatch plan evidence write failed",
215
+ workflowId,
216
+ planRevisionId,
217
+ roleCount: selectedAgentRoles.length,
218
+ taskCount: tasks.length,
219
+ });
220
+ const reloadAfter = reloadFlowDeskSessionEvidenceV1({
221
+ workflowId,
222
+ rootDir: input.config.rootDir,
223
+ });
224
+ const persisted = reloadAfter.ok &&
225
+ reloadAfter.entries.some((entry) => entry.evidenceClass === "workflow_dispatch_plan" &&
226
+ entry.evidenceId === planRevisionId);
227
+ if (!persisted)
228
+ return blocked({
229
+ config: input.config,
230
+ request,
231
+ reason: "workflow dispatch plan reload verification failed",
232
+ workflowId,
233
+ planRevisionId,
234
+ roleCount: selectedAgentRoles.length,
235
+ taskCount: tasks.length,
236
+ });
237
+ return {
238
+ status: "workflow_dispatch_plan_recorded",
239
+ rootDir: input.config.rootDir,
240
+ workflowId,
241
+ planRevisionId,
242
+ selectedAgentRoleCount: selectedAgentRoles.length,
243
+ taskCount: tasks.length,
244
+ writeAttempted: true,
245
+ evidenceReloaded: true,
246
+ summaryForUser: `FlowDesk workflow dispatch plan recorded for ${selectedAgentRoles.length} role(s) and ${tasks.length} task(s). Planning only: no dispatch, provider call, runtime execution, lane launch, or fallback authority was opened.`,
247
+ safeNextActions: safeNextActions(),
248
+ authority: authority(true),
249
+ };
250
+ }
251
+ //# sourceMappingURL=workflow-dispatch-plan-tool.js.map