@dv.nghiem/flowdeck 0.4.3 → 0.4.4

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.
@@ -1,3 +1,4 @@
1
+ import type { ActivityReporter } from "../services/activity-reporter";
1
2
  export declare function telemetryHook(context: {
2
3
  directory?: string;
3
4
  }, toolInput: {
@@ -11,7 +12,7 @@ export declare function telemetryHook(context: {
11
12
  runId?: string;
12
13
  }, output: {
13
14
  args?: Record<string, unknown>;
14
- }): Promise<void>;
15
+ }, reporter?: ActivityReporter): Promise<void>;
15
16
  export declare function telemetryAfterHook(context: {
16
17
  directory?: string;
17
18
  }, toolInput: {
@@ -28,5 +29,5 @@ export declare function telemetryAfterHook(context: {
28
29
  output?: string;
29
30
  metadata?: unknown;
30
31
  error?: unknown;
31
- }): Promise<void>;
32
+ }, reporter?: ActivityReporter): Promise<void>;
32
33
  //# sourceMappingURL=telemetry-hook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry-hook.d.ts","sourceRoot":"","sources":["../../src/hooks/telemetry-hook.ts"],"names":[],"mappings":"AAsCA,wBAAsB,aAAa,CACjC,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAC/B,SAAS,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3J,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACzC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAC/B,SAAS,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3J,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/E,OAAO,CAAC,IAAI,CAAC,CAaf"}
1
+ {"version":3,"file":"telemetry-hook.d.ts","sourceRoot":"","sources":["../../src/hooks/telemetry-hook.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAsErE,wBAAsB,aAAa,CACjC,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAC/B,SAAS,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3J,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC1C,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAC/B,SAAS,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3J,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EAChF,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAgDf"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAiHjD,QAAA,MAAM,MAAM,EAAE,MAyVb,CAAA;AAED,eAAe,MAAM,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAkHjD,QAAA,MAAM,MAAM,EAAE,MA6Wb,CAAA;AAED,eAAe,MAAM,CAAA"}
package/dist/index.js CHANGED
@@ -867,12 +867,215 @@ function isUiHeavyTask(input) {
867
867
  return !hasOnlyNonUiSignals;
868
868
  }
869
869
 
870
+ // src/services/activity-reporter.ts
871
+ var SUMMARY_MAX_NORMAL = 120;
872
+ var SUMMARY_MAX_DEBUG = 600;
873
+ var HEARTBEAT_INTERVAL_MS = 15000;
874
+ var TOAST_ON_START_TOOLS = new Set(["delegate", "run-pipeline"]);
875
+ function isDebugMode() {
876
+ return process.env.FLOWDECK_DEBUG === "true" || process.env.FLOWDECK_DEBUG === "1";
877
+ }
878
+ function summarize(text, maxLen = SUMMARY_MAX_NORMAL) {
879
+ if (!text)
880
+ return "";
881
+ const s = text.trim().replace(/\s+/g, " ");
882
+ return s.length <= maxLen ? s : s.slice(0, maxLen - 1) + "…";
883
+ }
884
+ function fmtDuration(ms) {
885
+ if (ms < 1000)
886
+ return `${ms}ms`;
887
+ return `${(ms / 1000).toFixed(1)}s`;
888
+ }
889
+ function normalizeCommandName(raw) {
890
+ return raw.replace(/^\//, "").replace(/^fd-/, "");
891
+ }
892
+
893
+ class ActivityReporter {
894
+ log;
895
+ toastFn;
896
+ startTimes = new Map;
897
+ heartbeats = new Map;
898
+ constructor(log, toast) {
899
+ this.log = log;
900
+ this.toastFn = toast;
901
+ }
902
+ emit(msg) {
903
+ try {
904
+ this.log(msg);
905
+ } catch {}
906
+ }
907
+ toastNow(msg, variant, duration) {
908
+ if (!this.toastFn)
909
+ return;
910
+ try {
911
+ this.toastFn(msg, variant, duration);
912
+ } catch {}
913
+ }
914
+ trackStart(key) {
915
+ this.startTimes.set(key, Date.now());
916
+ const toolName = key.split(":").pop() ?? key;
917
+ const interval = setInterval(() => {
918
+ const startMs = this.startTimes.get(key);
919
+ if (startMs === undefined)
920
+ return;
921
+ const elapsed = Date.now() - startMs;
922
+ const msg = `[⋯ ${toolName}] still running (${fmtDuration(elapsed)})`;
923
+ this.emit(msg);
924
+ this.toastNow(msg, "info", 8000);
925
+ }, HEARTBEAT_INTERVAL_MS);
926
+ if (typeof interval.unref === "function") {
927
+ interval.unref();
928
+ }
929
+ this.heartbeats.set(key, interval);
930
+ }
931
+ elapsedMs(key) {
932
+ const interval = this.heartbeats.get(key);
933
+ if (interval !== undefined) {
934
+ clearInterval(interval);
935
+ this.heartbeats.delete(key);
936
+ }
937
+ const t = this.startTimes.get(key);
938
+ if (t === undefined)
939
+ return;
940
+ this.startTimes.delete(key);
941
+ return Date.now() - t;
942
+ }
943
+ reportToolStarted(tool4, inputSummary, meta = {}) {
944
+ const maxLen = isDebugMode() ? SUMMARY_MAX_DEBUG : SUMMARY_MAX_NORMAL;
945
+ const parts = [`[→ ${tool4}]`];
946
+ if (meta.agent)
947
+ parts.push(`agent=${meta.agent}`);
948
+ if (inputSummary)
949
+ parts.push(summarize(inputSummary, maxLen));
950
+ if (isDebugMode()) {
951
+ if (meta.session_id)
952
+ parts.push(`session=${meta.session_id}`);
953
+ if (meta.stage)
954
+ parts.push(`stage=${meta.stage}`);
955
+ if (meta.run_id)
956
+ parts.push(`run=${meta.run_id}`);
957
+ }
958
+ this.emit(parts.join(" "));
959
+ if (TOAST_ON_START_TOOLS.has(tool4)) {
960
+ const agentPart = meta.agent ? ` @${meta.agent}` : "";
961
+ const inputPart = inputSummary ? `: ${summarize(inputSummary, 60)}` : "";
962
+ this.toastNow(`→ ${tool4}${agentPart}${inputPart}`, "info", 3000);
963
+ }
964
+ }
965
+ reportToolCompleted(tool4, durationMs, resultSummary, meta = {}) {
966
+ const maxLen = isDebugMode() ? SUMMARY_MAX_DEBUG : SUMMARY_MAX_NORMAL;
967
+ const dur = durationMs !== undefined ? ` (${fmtDuration(durationMs)})` : "";
968
+ const parts = [`[✓ ${tool4}]${dur}`];
969
+ if (meta.agent)
970
+ parts.push(`agent=${meta.agent}`);
971
+ if (resultSummary)
972
+ parts.push(summarize(resultSummary, maxLen));
973
+ if (isDebugMode() && meta.retry_count && meta.retry_count > 0) {
974
+ parts.push(`retries=${meta.retry_count}`);
975
+ }
976
+ this.emit(parts.join(" "));
977
+ }
978
+ reportToolFailed(tool4, durationMs, error, meta = {}) {
979
+ const dur = durationMs !== undefined ? ` (${fmtDuration(durationMs)})` : "";
980
+ const parts = [`[✗ ${tool4}]${dur}`];
981
+ if (meta.agent)
982
+ parts.push(`agent=${meta.agent}`);
983
+ parts.push(`error=${summarize(error, isDebugMode() ? SUMMARY_MAX_DEBUG : 200)}`);
984
+ if (isDebugMode() && meta.retry_count && meta.retry_count > 0) {
985
+ parts.push(`retries=${meta.retry_count}`);
986
+ }
987
+ this.emit(parts.join(" "));
988
+ this.toastNow(`✗ ${tool4}${dur}: ${summarize(error, 80)}`, "error", 8000);
989
+ }
990
+ reportToolRetried(tool4, attempt, reason, meta = {}) {
991
+ const parts = [`[↺ ${tool4}] retry attempt=${attempt}`];
992
+ if (meta.agent)
993
+ parts.push(`agent=${meta.agent}`);
994
+ if (reason)
995
+ parts.push(`reason=${summarize(reason, 80)}`);
996
+ this.emit(parts.join(" "));
997
+ this.toastNow(`↺ ${tool4} retry #${attempt}${meta.agent ? ` @${meta.agent}` : ""}`, "warning", 5000);
998
+ }
999
+ reportToolFallback(fromTool, toTool, reason, meta = {}) {
1000
+ const parts = [`[⇢ fallback] ${fromTool} → ${toTool}`];
1001
+ if (reason)
1002
+ parts.push(`reason=${summarize(reason, 80)}`);
1003
+ if (meta.agent)
1004
+ parts.push(`agent=${meta.agent}`);
1005
+ this.emit(parts.join(" "));
1006
+ this.toastNow(`⇢ fallback: ${fromTool} → ${toTool}`, "info", 4000);
1007
+ }
1008
+ reportCacheHit(tool4, agent, meta = {}) {
1009
+ const parts = [`[≡ ${tool4}] cache hit agent=${agent}`];
1010
+ if (isDebugMode() && meta.session_id)
1011
+ parts.push(`session=${meta.session_id}`);
1012
+ this.emit(parts.join(" "));
1013
+ }
1014
+ reportSkipped(tool4, reason, meta = {}) {
1015
+ const parts = [`[⊘ ${tool4}] skipped`];
1016
+ if (reason)
1017
+ parts.push(`reason=${summarize(reason, 80)}`);
1018
+ if (meta.agent)
1019
+ parts.push(`agent=${meta.agent}`);
1020
+ this.emit(parts.join(" "));
1021
+ }
1022
+ reportStageProgress(stage, status, detail, meta = {}) {
1023
+ const icon = {
1024
+ started: "▶",
1025
+ running: "⋯",
1026
+ complete: "●",
1027
+ failed: "✗",
1028
+ waiting: "⌛"
1029
+ };
1030
+ const sym = icon[status] ?? "·";
1031
+ const parts = [`[${sym} ${stage}] ${status}`];
1032
+ if (detail)
1033
+ parts.push(summarize(detail));
1034
+ if (isDebugMode() && meta.workflow_id)
1035
+ parts.push(`workflow=${meta.workflow_id}`);
1036
+ this.emit(parts.join(" "));
1037
+ const detailPart = detail ? `: ${summarize(detail, 60)}` : "";
1038
+ switch (status) {
1039
+ case "started":
1040
+ this.toastNow(`▶ ${stage} started${detailPart}`, "info", 3000);
1041
+ break;
1042
+ case "complete":
1043
+ this.toastNow(`● ${stage} complete${detailPart}`, "success", 4000);
1044
+ break;
1045
+ case "failed":
1046
+ this.toastNow(`✗ ${stage} failed${detailPart}`, "error", 8000);
1047
+ break;
1048
+ case "waiting":
1049
+ this.toastNow(`⌛ ${stage}: waiting for input${detailPart}`, "warning", 30000);
1050
+ break;
1051
+ }
1052
+ }
1053
+ reportWaitingForApproval(tool4, _meta = {}) {
1054
+ const msg = `⌛ Approval required: ${tool4}`;
1055
+ this.emit(msg);
1056
+ this.toastNow(msg, "warning", 30000);
1057
+ }
1058
+ reportCommandStarted(command) {
1059
+ const cmd = normalizeCommandName(command);
1060
+ const msg = `▶ /${cmd} started`;
1061
+ this.emit(msg);
1062
+ this.toastNow(msg, "info", 2500);
1063
+ }
1064
+ reportCommandCompleted(command, hasEdits) {
1065
+ const cmd = normalizeCommandName(command);
1066
+ const detail = hasEdits ? " (files modified)" : "";
1067
+ const msg = `● /${cmd} complete${detail}`;
1068
+ this.emit(msg);
1069
+ this.toastNow(msg, "success", 5000);
1070
+ }
1071
+ }
1072
+
870
1073
  // src/tools/run-pipeline.ts
871
1074
  function extractText(parts) {
872
1075
  return parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join(`
873
1076
  `);
874
1077
  }
875
- function createRunPipelineTool(client) {
1078
+ function createRunPipelineTool(client, reporter) {
876
1079
  return tool4({
877
1080
  description: "Run agents in sequential pipeline. Each step's output is appended to the next step's context. One fresh child session per step. Returns full trace with session ID, input/output/duration per step.",
878
1081
  args: {
@@ -893,6 +1096,8 @@ function createRunPipelineTool(client) {
893
1096
  let aborted = false;
894
1097
  const retryAttempts = typeof args.retry_attempts === "number" ? args.retry_attempts : 1;
895
1098
  const maxRetries = Math.max(0, Math.floor(retryAttempts));
1099
+ const totalSteps = args.steps.length;
1100
+ reporter?.reportStageProgress("pipeline", "started", `${totalSteps} step(s)`);
896
1101
  let inflightChildId = null;
897
1102
  const abortHandler = () => {
898
1103
  if (inflightChildId) {
@@ -904,7 +1109,8 @@ function createRunPipelineTool(client) {
904
1109
  };
905
1110
  context.abort.addEventListener("abort", abortHandler);
906
1111
  try {
907
- for (const step of args.steps) {
1112
+ for (let stepIdx = 0;stepIdx < args.steps.length; stepIdx++) {
1113
+ const step = args.steps[stepIdx];
908
1114
  if (context.abort.aborted) {
909
1115
  aborted = true;
910
1116
  break;
@@ -916,6 +1122,10 @@ function createRunPipelineTool(client) {
916
1122
  ---
917
1123
 
918
1124
  ${step.prompt}` : step.prompt;
1125
+ reporter?.reportToolStarted("run-pipeline", summarize(step.prompt, 80), {
1126
+ agent: step.agent,
1127
+ stage: `step ${stepIdx + 1}/${totalSteps}`
1128
+ });
919
1129
  const createRes = await client.session.create({
920
1130
  body: { parentID: context.sessionID, title: `${step.agent}-pipeline` },
921
1131
  query: { directory: context.directory }
@@ -923,6 +1133,7 @@ ${step.prompt}` : step.prompt;
923
1133
  if (createRes.error || !createRes.data?.id) {
924
1134
  const errMsg = `Failed to create session: ${createRes.error?.detail ?? "unknown"}`;
925
1135
  trace.push({ agent: step.agent, task_type: taskType, model: "", input: stepInput, output: errMsg, duration_ms: Date.now() - stepStart, success: false });
1136
+ reporter?.reportToolFailed("run-pipeline", Date.now() - stepStart, errMsg, { agent: step.agent });
926
1137
  aborted = true;
927
1138
  break;
928
1139
  }
@@ -942,6 +1153,7 @@ ${step.prompt}` : step.prompt;
942
1153
  if (!shouldRetry(promptRes) || attempt === maxRetries)
943
1154
  break;
944
1155
  retriesUsed++;
1156
+ reporter?.reportToolRetried("run-pipeline", retriesUsed, "prompt response indicated retry", { agent: step.agent });
945
1157
  }
946
1158
  inflightChildId = null;
947
1159
  if (context.abort.aborted) {
@@ -952,6 +1164,7 @@ ${step.prompt}` : step.prompt;
952
1164
  const errMsg = `Prompt failed: ${promptRes?.error?.detail ?? "unknown"}`;
953
1165
  trace.push({ agent: step.agent, session_id: createRes.data.id, task_type: taskType, model: "", input: stepInput, output: `${errMsg}${retriesUsed > 0 ? ` (retries: ${retriesUsed})` : ""}`, duration_ms: Date.now() - stepStart, success: false });
954
1166
  recordRun(context.directory, step.agent, "", taskType, false, Date.now() - stepStart);
1167
+ reporter?.reportToolFailed("run-pipeline", Date.now() - stepStart, errMsg, { agent: step.agent, retry_count: retriesUsed });
955
1168
  if (args.abort_on_failure) {
956
1169
  aborted = true;
957
1170
  break;
@@ -963,6 +1176,7 @@ ${step.prompt}` : step.prompt;
963
1176
  const errMsg = `Agent error: ${JSON.stringify(info.error)}`;
964
1177
  trace.push({ agent: step.agent, session_id: createRes.data.id, task_type: taskType, model: "", input: stepInput, output: `${errMsg}${retriesUsed > 0 ? ` (retries: ${retriesUsed})` : ""}`, duration_ms: Date.now() - stepStart, success: false });
965
1178
  recordRun(context.directory, step.agent, "", taskType, false, Date.now() - stepStart);
1179
+ reporter?.reportToolFailed("run-pipeline", Date.now() - stepStart, errMsg, { agent: step.agent, retry_count: retriesUsed });
966
1180
  if (args.abort_on_failure) {
967
1181
  aborted = true;
968
1182
  break;
@@ -972,15 +1186,26 @@ ${step.prompt}` : step.prompt;
972
1186
  const output = extractText(promptRes.data?.parts ?? []);
973
1187
  trace.push({ agent: step.agent, session_id: createRes.data.id, task_type: taskType, model: "", input: stepInput, output: output || "(no text output)", duration_ms: Date.now() - stepStart, success: true, context_chars: carryContext.length });
974
1188
  recordRun(context.directory, step.agent, "", taskType, true, Date.now() - stepStart);
1189
+ reporter?.reportToolCompleted("run-pipeline", Date.now() - stepStart, summarize(output, 80), {
1190
+ agent: step.agent,
1191
+ retry_count: retriesUsed,
1192
+ stage: `step ${stepIdx + 1}/${totalSteps}`
1193
+ });
975
1194
  const rawOutput = output || "";
976
1195
  carryContext = typeof args.max_carry_chars === "number" && rawOutput.length > args.max_carry_chars ? rawOutput.slice(rawOutput.length - args.max_carry_chars) : rawOutput;
977
1196
  }
978
1197
  } finally {
979
1198
  context.abort.removeEventListener("abort", abortHandler);
980
1199
  }
1200
+ const totalDuration = Date.now() - startTime;
1201
+ if (aborted) {
1202
+ reporter?.reportStageProgress("pipeline", "failed", `aborted after ${trace.length}/${totalSteps} steps`);
1203
+ } else {
1204
+ reporter?.reportStageProgress("pipeline", "complete", `${totalSteps} step(s) in ${totalDuration}ms`);
1205
+ }
981
1206
  return JSON.stringify({
982
1207
  steps: trace,
983
- total_duration_ms: Date.now() - startTime,
1208
+ total_duration_ms: totalDuration,
984
1209
  aborted
985
1210
  });
986
1211
  }
@@ -1386,7 +1611,7 @@ function extractText2(parts) {
1386
1611
  return parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join(`
1387
1612
  `);
1388
1613
  }
1389
- function createDelegateTool(client) {
1614
+ function createDelegateTool(client, reporter) {
1390
1615
  return tool5({
1391
1616
  description: "Delegate a task to a single agent via a child session. Returns the agent's output.",
1392
1617
  args: {
@@ -1417,6 +1642,10 @@ function createDelegateTool(client) {
1417
1642
  ---
1418
1643
 
1419
1644
  ${args.prompt}` : args.prompt;
1645
+ reporter?.reportToolStarted("delegate", summarize(args.prompt, 100), {
1646
+ agent: args.agent,
1647
+ stage: args.stage
1648
+ });
1420
1649
  const safe_to_cache = args.safe_to_cache === true && CACHEABLE_AGENTS.has(args.agent);
1421
1650
  let stateVersion = 0;
1422
1651
  let indexVersion = 0;
@@ -1432,6 +1661,7 @@ ${args.prompt}` : args.prompt;
1432
1661
  if (metricsWorkflowId) {
1433
1662
  recordCacheHit(context.directory, metricsWorkflowId, metricsStage, fullPrompt, args.agent, agentModel);
1434
1663
  }
1664
+ reporter?.reportCacheHit("delegate", args.agent);
1435
1665
  return JSON.stringify({
1436
1666
  agent: args.agent,
1437
1667
  success: true,
@@ -1489,14 +1719,20 @@ ${args.prompt}` : args.prompt;
1489
1719
  recordRetryCall(context.directory, metricsWorkflowId, metricsStage, fullPromptForSession, "", args.agent, Date.now() - attemptStart, agentModel, retryCostUsd);
1490
1720
  }
1491
1721
  retriesUsed++;
1722
+ reporter?.reportToolRetried("delegate", retriesUsed, "prompt response indicated retry", { agent: args.agent });
1492
1723
  }
1493
1724
  if (!promptRes || promptRes.error) {
1725
+ const errMsg = `Prompt failed: ${promptRes?.error?.detail ?? "unknown"}`;
1494
1726
  recordRun(context.directory, args.agent, "", taskType, false, Date.now() - startTime);
1727
+ reporter?.reportToolFailed("delegate", Date.now() - startTime, errMsg, {
1728
+ agent: args.agent,
1729
+ retry_count: retriesUsed
1730
+ });
1495
1731
  return JSON.stringify({
1496
1732
  agent: args.agent,
1497
1733
  session_id: childId,
1498
1734
  success: false,
1499
- error: `Prompt failed: ${promptRes?.error?.detail ?? "unknown"}`,
1735
+ error: errMsg,
1500
1736
  task_type: taskType,
1501
1737
  model: "",
1502
1738
  retries_used: retriesUsed,
@@ -1505,12 +1741,17 @@ ${args.prompt}` : args.prompt;
1505
1741
  }
1506
1742
  const info = promptRes.data?.info;
1507
1743
  if (info?.error) {
1744
+ const errMsg = `Agent error: ${JSON.stringify(info.error)}`;
1508
1745
  recordRun(context.directory, args.agent, "", taskType, false, Date.now() - startTime);
1746
+ reporter?.reportToolFailed("delegate", Date.now() - startTime, errMsg, {
1747
+ agent: args.agent,
1748
+ retry_count: retriesUsed
1749
+ });
1509
1750
  return JSON.stringify({
1510
1751
  agent: args.agent,
1511
1752
  session_id: childId,
1512
1753
  success: false,
1513
- error: `Agent error: ${JSON.stringify(info.error)}`,
1754
+ error: errMsg,
1514
1755
  task_type: taskType,
1515
1756
  model: "",
1516
1757
  retries_used: retriesUsed,
@@ -1525,6 +1766,10 @@ ${args.prompt}` : args.prompt;
1525
1766
  const costUsd = agentModel ? estimateCostUSD(agentModel, inputTokens, outputTokens) : undefined;
1526
1767
  recordModelCall(context.directory, metricsWorkflowId, metricsStage, fullPromptForSession, output, args.agent, Date.now() - startTime, agentModel, costUsd);
1527
1768
  }
1769
+ reporter?.reportToolCompleted("delegate", Date.now() - startTime, summarize(output, 80), {
1770
+ agent: args.agent,
1771
+ retry_count: retriesUsed
1772
+ });
1528
1773
  if (safe_to_cache && output) {
1529
1774
  setCached(context.directory, args.agent, fullPromptForSession, args.context ?? "", stateVersion, indexVersion, output, true, args.cache_ttl_ms);
1530
1775
  }
@@ -3445,7 +3690,7 @@ var COMPLETION_COMMANDS = new Set([
3445
3690
  "execute",
3446
3691
  "verify"
3447
3692
  ]);
3448
- function normalizeCommandName(raw) {
3693
+ function normalizeCommandName2(raw) {
3449
3694
  return raw.replace(/^\//, "").replace(/^fd-/, "");
3450
3695
  }
3451
3696
  function notify(title, body, level = "info") {
@@ -3490,7 +3735,7 @@ class NotificationController {
3490
3735
  this.log = log;
3491
3736
  }
3492
3737
  onCommandExecuted(rawCommand) {
3493
- const name = normalizeCommandName(rawCommand);
3738
+ const name = normalizeCommandName2(rawCommand);
3494
3739
  if (!INTERACTIVE_COMMANDS.has(name) && !COMPLETION_COMMANDS.has(name)) {
3495
3740
  this.log(`[notify] command.executed: "${name}" — not a tracked command, skipping`);
3496
3741
  return;
@@ -3717,6 +3962,32 @@ function appendEvent2(dir, partial) {
3717
3962
  }
3718
3963
 
3719
3964
  // src/hooks/telemetry-hook.ts
3965
+ var toolStartTimes = new Map;
3966
+ var REPORTABLE_TOOLS = new Set([
3967
+ "delegate",
3968
+ "run-pipeline",
3969
+ "council",
3970
+ "bash",
3971
+ "write",
3972
+ "edit",
3973
+ "read",
3974
+ "codegraph",
3975
+ "codebase-state",
3976
+ "planning-state",
3977
+ "workspace-state",
3978
+ "repo-memory",
3979
+ "hash-edit",
3980
+ "context-generator",
3981
+ "volatility-map",
3982
+ "failure-replay",
3983
+ "decision-trace",
3984
+ "policy-engine",
3985
+ "reflect"
3986
+ ]);
3987
+ var SELF_LOGGING_TOOLS = new Set(["delegate", "run-pipeline", "council"]);
3988
+ function correlationKey(sessionId, runId, tool19) {
3989
+ return `${sessionId}:${runId}:${tool19}`;
3990
+ }
3720
3991
  function resolveIds(toolInput) {
3721
3992
  const session_id = toolInput.sessionID ?? toolInput.sessionId ?? process.env.OPENCODE_SESSION_ID ?? "session-0";
3722
3993
  const run_id = toolInput.messageID ?? toolInput.messageId ?? toolInput.runID ?? toolInput.runId ?? process.env.OPENCODE_RUN_ID ?? "run-0";
@@ -3739,10 +4010,24 @@ function inferStatus(output) {
3739
4010
  return "ok";
3740
4011
  }
3741
4012
  }
3742
- async function telemetryHook(context, toolInput, output) {
4013
+ function buildInputSummary(tool19, args) {
4014
+ if (tool19 === "delegate" || tool19 === "run-pipeline" || tool19 === "council") {
4015
+ return "";
4016
+ }
4017
+ if (tool19 === "bash" || tool19 === "write" || tool19 === "edit" || tool19 === "read") {
4018
+ const path = args.path ?? args.filePath ?? args.file ?? "";
4019
+ const cmd = args.command ?? args.cmd ?? "";
4020
+ return path || cmd ? summarize(String(path || cmd), 80) : "";
4021
+ }
4022
+ const firstStr = Object.values(args).find((v) => typeof v === "string");
4023
+ return firstStr ? summarize(firstStr, 80) : "";
4024
+ }
4025
+ async function telemetryHook(context, toolInput, output, reporter) {
3743
4026
  const dir = context.directory ?? process.cwd();
3744
4027
  const tool19 = toolInput.name ?? toolInput.tool ?? "unknown";
3745
4028
  const ids = resolveIds(toolInput);
4029
+ const key = correlationKey(ids.session_id, ids.run_id, tool19);
4030
+ toolStartTimes.set(key, Date.now());
3746
4031
  appendEvent2(dir, {
3747
4032
  session_id: ids.session_id,
3748
4033
  run_id: ids.run_id,
@@ -3751,19 +4036,56 @@ async function telemetryHook(context, toolInput, output) {
3751
4036
  status: "ok",
3752
4037
  meta: { parameters: output.args ?? {} }
3753
4038
  });
4039
+ if (reporter && REPORTABLE_TOOLS.has(tool19) && !SELF_LOGGING_TOOLS.has(tool19)) {
4040
+ const inputSummary = buildInputSummary(tool19, output.args ?? {});
4041
+ reporter.reportToolStarted(tool19, inputSummary, {
4042
+ session_id: ids.session_id,
4043
+ run_id: ids.run_id
4044
+ });
4045
+ }
4046
+ if (reporter && REPORTABLE_TOOLS.has(tool19)) {
4047
+ reporter.trackStart(key);
4048
+ }
3754
4049
  }
3755
- async function telemetryAfterHook(context, toolInput, output) {
4050
+ async function telemetryAfterHook(context, toolInput, output, reporter) {
3756
4051
  const dir = context.directory ?? process.cwd();
3757
4052
  const tool19 = toolInput.name ?? toolInput.tool ?? "unknown";
3758
4053
  const ids = resolveIds(toolInput);
4054
+ const key = correlationKey(ids.session_id, ids.run_id, tool19);
3759
4055
  const status = inferStatus(output);
4056
+ let duration_ms;
4057
+ if (reporter && REPORTABLE_TOOLS.has(tool19)) {
4058
+ duration_ms = reporter.elapsedMs(key);
4059
+ }
4060
+ if (duration_ms === undefined) {
4061
+ const startMs = toolStartTimes.get(key);
4062
+ if (startMs !== undefined)
4063
+ duration_ms = Date.now() - startMs;
4064
+ }
4065
+ toolStartTimes.delete(key);
4066
+ let result_summary;
4067
+ if (typeof output.output === "string") {
4068
+ result_summary = summarize(output.output, 100);
4069
+ } else if (output.error) {
4070
+ result_summary = summarize(String(output.error), 100);
4071
+ }
3760
4072
  appendEvent2(dir, {
3761
4073
  session_id: ids.session_id,
3762
4074
  run_id: ids.run_id,
3763
- event: "tool.complete",
4075
+ event: status === "error" ? "tool.failed" : "tool.complete",
3764
4076
  tool: tool19,
3765
- status
4077
+ status,
4078
+ duration_ms,
4079
+ result_summary
3766
4080
  });
4081
+ if (reporter && REPORTABLE_TOOLS.has(tool19) && !SELF_LOGGING_TOOLS.has(tool19)) {
4082
+ if (status === "error") {
4083
+ const errText = output.error ? String(output.error) : typeof output.output === "string" ? output.output : "unknown error";
4084
+ reporter.reportToolFailed(tool19, duration_ms, errText);
4085
+ } else {
4086
+ reporter.reportToolCompleted(tool19, duration_ms, result_summary ?? "");
4087
+ }
4088
+ }
3767
4089
  }
3768
4090
 
3769
4091
  // src/services/approval-manager.ts
@@ -8197,8 +8519,11 @@ function loadCommands() {
8197
8519
  }
8198
8520
  var plugin = async (input, _options) => {
8199
8521
  const { directory, client, worktree } = input;
8200
- const runPipelineTool = createRunPipelineTool(client);
8201
- const delegateTool = createDelegateTool(client);
8522
+ const appLog = (msg) => client.app.log({ body: { service: "flowdeck", level: "info", message: msg } }).catch(() => {});
8523
+ const toastFn = (message, variant, duration) => client.tui.showToast({ body: { message, variant, duration }, query: { directory } }).catch(() => {});
8524
+ const activityReporter = new ActivityReporter(appLog, toastFn);
8525
+ const runPipelineTool = createRunPipelineTool(client, activityReporter);
8526
+ const delegateTool = createDelegateTool(client, activityReporter);
8202
8527
  const councilTool = createCouncilTool(client);
8203
8528
  const fileTracker = new SessionFileTracker;
8204
8529
  const { fileEdited, fileWatcherUpdated } = createFileTrackerHooks(fileTracker);
@@ -8208,11 +8533,11 @@ var plugin = async (input, _options) => {
8208
8533
  const sessionIdleHook = createSessionIdleHook(client, fileTracker);
8209
8534
  const compactionHook = createCompactionHook({ directory }, fileTracker);
8210
8535
  const orchestratorGuard = new OrchestratorGuard;
8211
- const appLog = (msg) => client.app.log({ body: { service: "flowdeck", level: "info", message: msg } }).catch(() => {});
8212
8536
  const autoLearnHook = createAutoLearnHook(client, fileTracker, directory, appLog);
8213
8537
  const notifCtrl = new NotificationController(undefined, appLog);
8214
8538
  const agentConfigs = getAgentConfigs({});
8215
8539
  const mcps = createFlowDeckMcps();
8540
+ let lastExecutedCommand = null;
8216
8541
  return {
8217
8542
  name: "@dv.nghiem/flowdeck",
8218
8543
  agent: agentConfigs,
@@ -8315,9 +8640,13 @@ var plugin = async (input, _options) => {
8315
8640
  "file.edited": fileEdited,
8316
8641
  "file.watcher.updated": fileWatcherUpdated,
8317
8642
  "experimental.session.compacting": compactionHook,
8318
- "command.execute.before": async (_input, _output) => {},
8643
+ "command.execute.before": async (input2, _output) => {
8644
+ activityReporter.reportCommandStarted(input2.command);
8645
+ lastExecutedCommand = input2.command;
8646
+ },
8319
8647
  "permission.ask": async (input2, _output) => {
8320
8648
  notifyPermissionNeeded(input2.title);
8649
+ activityReporter.reportWaitingForApproval(input2.title);
8321
8650
  },
8322
8651
  event: async ({ event }) => {
8323
8652
  const type = event?.type ?? "";
@@ -8334,6 +8663,10 @@ var plugin = async (input, _options) => {
8334
8663
  orchestratorGuard.onEvent(event);
8335
8664
  if (type === "session.idle") {
8336
8665
  const hasEdits = fileTracker.getEditedPaths().length > 0;
8666
+ if (lastExecutedCommand) {
8667
+ activityReporter.reportCommandCompleted(lastExecutedCommand, hasEdits);
8668
+ lastExecutedCommand = null;
8669
+ }
8337
8670
  notifCtrl.onSessionIdle(hasEdits);
8338
8671
  try {
8339
8672
  await sessionIdleHook();
@@ -8343,6 +8676,7 @@ var plugin = async (input, _options) => {
8343
8676
  }
8344
8677
  }
8345
8678
  if (type === "session.error") {
8679
+ lastExecutedCommand = null;
8346
8680
  const err = event?.properties?.error;
8347
8681
  const errorMsg = (err && typeof err === "object" && "message" in err ? String(err.message) : undefined) ?? (typeof err === "string" ? err : undefined) ?? "An unexpected error occurred";
8348
8682
  notifCtrl.onSessionError(errorMsg);
@@ -8387,7 +8721,7 @@ var plugin = async (input, _options) => {
8387
8721
  }
8388
8722
  }
8389
8723
  }
8390
- await telemetryHook({ directory }, toolInput, toolOutput);
8724
+ await telemetryHook({ directory }, toolInput, toolOutput, activityReporter);
8391
8725
  await approvalHook({ directory }, toolInput, toolOutput);
8392
8726
  await guardRailsHook({ directory }, toolInput, toolOutput);
8393
8727
  await toolGuardHook({ directory }, toolInput, toolOutput);
@@ -8395,7 +8729,7 @@ var plugin = async (input, _options) => {
8395
8729
  await decisionTraceHook({ directory }, toolInput, toolOutput);
8396
8730
  },
8397
8731
  "tool.execute.after": async (toolInput, toolOutput) => {
8398
- await telemetryAfterHook({ directory }, toolInput, toolOutput);
8732
+ await telemetryAfterHook({ directory }, toolInput, toolOutput, activityReporter);
8399
8733
  const afterToolName = toolInput.tool ?? toolInput.name ?? "";
8400
8734
  if (afterToolName === "delegate" || afterToolName === "run-pipeline") {
8401
8735
  try {
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Activity Reporter
3
+ *
4
+ * Surfaces tool lifecycle events and workflow-stage progress to the user
5
+ * in real-time via two channels:
6
+ * 1. appLog (client.app.log) — persistent server log visible in the log panel
7
+ * 2. toast (client.tui.showToast) — ephemeral in-TUI notifications for key events
8
+ *
9
+ * Design goals:
10
+ * - Every significant tool call emits a concise user-visible log line
11
+ * - Key events (failures, stage transitions, waiting states) also show TUI toasts
12
+ * - Heartbeat toast fires if a tracked tool runs > HEARTBEAT_INTERVAL_MS
13
+ * - Normal mode: short summaries (no raw dumps)
14
+ * - Debug mode (FLOWDECK_DEBUG=true): full inputs/outputs + trace metadata
15
+ * - Retries, fallbacks, cache hits, and skips are all individually logged
16
+ */
17
+ /** Interval before the first "still running" heartbeat toast (ms) */
18
+ export declare const HEARTBEAT_INTERVAL_MS = 15000;
19
+ export type ToastVariant = "info" | "success" | "warning" | "error";
20
+ /** Injectable toast function — wraps client.tui.showToast in production. */
21
+ export type ToastFn = (msg: string, variant: ToastVariant, duration?: number) => void;
22
+ export declare function isDebugMode(): boolean;
23
+ /** Trim text to maxLen, appending ellipsis when truncated */
24
+ export declare function summarize(text: string, maxLen?: number): string;
25
+ /** Format milliseconds as human-readable "42ms" or "3.2s" */
26
+ export declare function fmtDuration(ms: number): string;
27
+ export interface ActivityMeta {
28
+ session_id?: string;
29
+ run_id?: string;
30
+ command?: string;
31
+ stage?: string;
32
+ agent?: string;
33
+ retry_count?: number;
34
+ workflow_id?: string;
35
+ tool?: string;
36
+ }
37
+ export declare class ActivityReporter {
38
+ private readonly log;
39
+ private readonly toastFn?;
40
+ /** correlationKey → start epoch ms */
41
+ private readonly startTimes;
42
+ /** correlationKey → heartbeat interval handle */
43
+ private readonly heartbeats;
44
+ constructor(log: (msg: string) => void, toast?: ToastFn);
45
+ private emit;
46
+ /** Send an ephemeral toast to the TUI. Duration is in milliseconds. */
47
+ private toastNow;
48
+ /**
49
+ * Record start time against a correlation key and start a heartbeat interval.
50
+ * If the tracked operation hasn't finished within HEARTBEAT_INTERVAL_MS, a
51
+ * "still running" log line and toast are emitted to prevent the TUI from
52
+ * looking frozen during long-running tools.
53
+ */
54
+ trackStart(key: string): void;
55
+ /**
56
+ * Consume the start time for key, cancel its heartbeat, and return elapsed ms.
57
+ * Returns undefined if key was never tracked.
58
+ */
59
+ elapsedMs(key: string): number | undefined;
60
+ /** Emitted when a tool call begins. */
61
+ reportToolStarted(tool: string, inputSummary: string, meta?: ActivityMeta): void;
62
+ /** Emitted when a tool call completes successfully. */
63
+ reportToolCompleted(tool: string, durationMs: number | undefined, resultSummary: string, meta?: ActivityMeta): void;
64
+ /** Emitted when a tool call fails (after all retries are exhausted). */
65
+ reportToolFailed(tool: string, durationMs: number | undefined, error: string, meta?: ActivityMeta): void;
66
+ /** Emitted each time a tool call is retried. */
67
+ reportToolRetried(tool: string, attempt: number, reason: string, meta?: ActivityMeta): void;
68
+ /** Emitted when the system falls back from one tool/strategy to another. */
69
+ reportToolFallback(fromTool: string, toTool: string, reason: string, meta?: ActivityMeta): void;
70
+ /** Emitted when a tool call is satisfied from the prompt cache. */
71
+ reportCacheHit(tool: string, agent: string, meta?: ActivityMeta): void;
72
+ /** Emitted when a tool or step is intentionally skipped. */
73
+ reportSkipped(tool: string, reason: string, meta?: ActivityMeta): void;
74
+ /**
75
+ * Report high-level workflow stage transitions so users can see
76
+ * which phase of a long-running workflow is currently active.
77
+ * Key status values (started/complete/failed/waiting) also emit TUI toasts.
78
+ */
79
+ reportStageProgress(stage: string, status: "started" | "running" | "complete" | "failed" | "waiting", detail?: string, meta?: ActivityMeta): void;
80
+ /**
81
+ * Emitted when the permission.ask hook fires — the system is blocked
82
+ * on user approval. Shows a prominent warning toast.
83
+ */
84
+ reportWaitingForApproval(tool: string, _meta?: ActivityMeta): void;
85
+ /**
86
+ * Emitted when a user command begins execution.
87
+ * Shows a brief info toast so the user knows the system heard them.
88
+ */
89
+ reportCommandStarted(command: string): void;
90
+ /**
91
+ * Emitted when a user command completes (session.idle fires after it).
92
+ * Shows a success toast with a note about file modifications.
93
+ */
94
+ reportCommandCompleted(command: string, hasEdits: boolean): void;
95
+ }
96
+ //# sourceMappingURL=activity-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-reporter.d.ts","sourceRoot":"","sources":["../../src/services/activity-reporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=activity-reporter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-reporter.test.d.ts","sourceRoot":"","sources":["../../src/services/activity-reporter.test.ts"],"names":[],"mappings":""}
@@ -1,4 +1,4 @@
1
- export type TelemetryEventType = "command.start" | "command.end" | "tool.call" | "tool.complete" | "agent.dispatch" | "agent.complete" | "approval.request" | "approval.resolve" | "run.complete" | "run.fail" | "policy.violation" | "patch.scored" | "contract.violation" | "agent.span.open" | "agent.span.close" | "budget.warning" | "budget.exhausted" | "deadlock.detected" | "scorecard.generated" | "supervisor.review";
1
+ export type TelemetryEventType = "command.start" | "command.end" | "tool.call" | "tool.complete" | "tool.failed" | "tool.retried" | "tool.fallback" | "cache.hit" | "stage.progress" | "agent.dispatch" | "agent.complete" | "approval.request" | "approval.resolve" | "run.complete" | "run.fail" | "policy.violation" | "patch.scored" | "contract.violation" | "agent.span.open" | "agent.span.close" | "budget.warning" | "budget.exhausted" | "deadlock.detected" | "scorecard.generated" | "supervisor.review";
2
2
  export interface TelemetryEvent {
3
3
  id: string;
4
4
  ts: string;
@@ -15,6 +15,14 @@ export interface TelemetryEvent {
15
15
  files?: string[];
16
16
  cost_estimate?: number;
17
17
  error_category?: string;
18
+ /** Short summary of the tool's input (no raw dumps) */
19
+ input_summary?: string;
20
+ /** Short summary of the tool's result */
21
+ result_summary?: string;
22
+ /** Workflow stage (e.g. "research", "plan", "execute") */
23
+ stage?: string;
24
+ /** Number of retry attempts consumed */
25
+ retry_count?: number;
18
26
  /** Estimated input tokens (chars / 4) for this event. */
19
27
  input_tokens?: number;
20
28
  /** Estimated output tokens (chars / 4) for this event. */
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/services/telemetry.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,kBAAkB,GAC1B,eAAe,GACf,aAAa,GACb,WAAW,GACX,eAAe,GACf,gBAAgB,GAChB,gBAAgB,GAChB,kBAAkB,GAClB,kBAAkB,GAClB,cAAc,GACd,UAAU,GACV,kBAAkB,GAClB,cAAc,GAEd,oBAAoB,GACpB,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,kBAAkB,GAClB,mBAAmB,GACnB,qBAAqB,GAErB,mBAAmB,CAAA;AAEvB,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,kBAAkB,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAA;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC,GAAG,cAAc,GAAG,IAAI,CAa1G;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,cAAc,EAAE,CAUrE;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,CAE1E;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,SAAM,GAAG,cAAc,EAAE,CAuBxE;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,cAAc,EAAE,CAI/E"}
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/services/telemetry.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,kBAAkB,GAC1B,eAAe,GACf,aAAa,GACb,WAAW,GACX,eAAe,GACf,aAAa,GACb,cAAc,GACd,eAAe,GACf,WAAW,GACX,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,kBAAkB,GAClB,kBAAkB,GAClB,cAAc,GACd,UAAU,GACV,kBAAkB,GAClB,cAAc,GAEd,oBAAoB,GACpB,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,kBAAkB,GAClB,mBAAmB,GACnB,qBAAqB,GAErB,mBAAmB,CAAA;AAEvB,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,kBAAkB,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAA;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,yCAAyC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC,GAAG,cAAc,GAAG,IAAI,CAa1G;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,cAAc,EAAE,CAUrE;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,CAE1E;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,SAAM,GAAG,cAAc,EAAE,CAuBxE;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,cAAc,EAAE,CAI/E"}
@@ -1,4 +1,5 @@
1
1
  import { type ToolDefinition } from "@opencode-ai/plugin";
2
2
  import type { OpencodeClient } from "@opencode-ai/sdk";
3
- export declare function createDelegateTool(client: OpencodeClient): ToolDefinition;
3
+ import { ActivityReporter } from "../services/activity-reporter";
4
+ export declare function createDelegateTool(client: OpencodeClient, reporter?: ActivityReporter | null): ToolDefinition;
4
5
  //# sourceMappingURL=delegate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../../src/tools/delegate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAmBtD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAmPzE"}
1
+ {"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../../src/tools/delegate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAWtD,OAAO,EAAE,gBAAgB,EAAa,MAAM,+BAA+B,CAAA;AAS3E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,cAAc,CA0Q7G"}
@@ -1,4 +1,5 @@
1
1
  import { type ToolDefinition } from "@opencode-ai/plugin";
2
2
  import type { OpencodeClient } from "@opencode-ai/sdk";
3
- export declare function createRunPipelineTool(client: OpencodeClient): ToolDefinition;
3
+ import { ActivityReporter } from "../services/activity-reporter";
4
+ export declare function createRunPipelineTool(client: OpencodeClient, reporter?: ActivityReporter | null): ToolDefinition;
4
5
  //# sourceMappingURL=run-pipeline.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-pipeline.d.ts","sourceRoot":"","sources":["../../src/tools/run-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AA8BtD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAkI5E"}
1
+ {"version":3,"file":"run-pipeline.d.ts","sourceRoot":"","sources":["../../src/tools/run-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAGtD,OAAO,EAAE,gBAAgB,EAAa,MAAM,+BAA+B,CAAA;AA4B3E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,cAAc,CA2JhH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dv.nghiem/flowdeck",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "FlowDeck — structured planning and execution workflows for OpenCode",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",