fifony 0.1.42 → 0.1.43

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 (58) hide show
  1. package/app/dist/assets/{CommandPalette-DNR5umI1.js → CommandPalette-M4VAMxCU.js} +1 -1
  2. package/app/dist/assets/{KeyboardShortcutsHelp-Dpl19F20.js → KeyboardShortcutsHelp-DkvPUXQq.js} +1 -1
  3. package/app/dist/assets/OnboardingWizard-B7V9hoCR.js +1 -0
  4. package/app/dist/assets/analytics.lazy-zVJdF880.js +1 -0
  5. package/app/dist/assets/{api-ChEctgc5.js → api-CkVfYg_m.js} +1 -1
  6. package/app/dist/assets/{createLucideIcon-R47sXufx.js → createLucideIcon-Dfk_Hxud.js} +1 -1
  7. package/app/dist/assets/index-BpiCi7Ew.css +1 -0
  8. package/app/dist/assets/index-D2INW0zc.js +47 -0
  9. package/app/dist/assets/vendor-BEoYbFV1.js +9 -0
  10. package/app/dist/index.html +5 -5
  11. package/app/dist/service-worker.js +9 -4
  12. package/bin/fifony.js +3 -0
  13. package/dist/agent/pty-daemon.js +177 -0
  14. package/dist/agent/run-local.js +177 -43
  15. package/dist/{agent-NNGZEKZH.js → agent-RMQTTUEC.js} +37 -16
  16. package/dist/analytics-broadcaster-O6YBP66L.js +145 -0
  17. package/dist/chunk-3NE23NYW.js +82 -0
  18. package/dist/chunk-42AMQAJG.js +404 -0
  19. package/dist/{chunk-H5N7O5NP.js → chunk-AILXZ2TD.js} +79 -147
  20. package/dist/{chunk-I2UHVKHS.js → chunk-BRSR26VK.js} +2 -2
  21. package/dist/chunk-E2EWEYA4.js +1302 -0
  22. package/dist/chunk-ESWHDHH6.js +102 -0
  23. package/dist/{chunk-NB44PCD2.js → chunk-FJNH3G2Z.js} +1061 -1138
  24. package/dist/chunk-MVTGAKQK.js +493 -0
  25. package/dist/chunk-QQQLP3PL.js +155 -0
  26. package/dist/chunk-SOBLO4YZ.js +2016 -0
  27. package/dist/chunk-YRSH2CLW.js +13784 -0
  28. package/dist/cli.js +335 -44
  29. package/dist/{issue-state-machine-GPQNZYUZ.js → fsm-issue-YGGF7SIL.js} +9 -5
  30. package/dist/helpers-L7NYO5XS.js +53 -0
  31. package/dist/issue-log-broadcaster-WZAHISYB.js +84 -0
  32. package/dist/{issues-MZLRSXD6.js → issues-3QRR7KM6.js} +10 -8
  33. package/dist/log-analyzer-K7MXQB4T.js +287 -0
  34. package/dist/mcp/server.js +109 -137
  35. package/dist/parallel-executor-6INE6NDO.js +118 -0
  36. package/dist/pid-manager-UBWXVSMD.js +21 -0
  37. package/dist/queue-workers-XFZK3TT5.js +32 -0
  38. package/dist/replan-issue.command-4UCWYHGZ.js +15 -0
  39. package/dist/scheduler-ZP7GOZDW.js +26 -0
  40. package/dist/{settings-NGY33WQE.js → settings-ZAWDCFP2.js} +32 -8
  41. package/dist/settings.resource-5CW456AZ.js +24 -0
  42. package/dist/store-M6NCKMZY.js +97 -0
  43. package/dist/{web-push-CRVDJKWR.js → web-push-AX5IIK3P.js} +2 -2
  44. package/dist/{workspace-D3F3XGSI.js → workspace-CJTWFWTJ.js} +5 -4
  45. package/package.json +8 -7
  46. package/app/dist/assets/OnboardingWizard-CijMhJDW.js +0 -1
  47. package/app/dist/assets/analytics.lazy-Dq90a756.js +0 -1
  48. package/app/dist/assets/index-Dy_fM427.js +0 -54
  49. package/app/dist/assets/index-Q9jBP0Pz.css +0 -1
  50. package/app/dist/assets/vendor-DkWeBvNl.js +0 -9
  51. package/dist/chunk-2CVTK5F2.js +0 -288
  52. package/dist/chunk-37N5OFHM.js +0 -125
  53. package/dist/chunk-JTKUWIQD.js +0 -8406
  54. package/dist/chunk-RBDBGU2C.js +0 -303
  55. package/dist/issue-runner-CMZPSVC7.js +0 -16
  56. package/dist/queue-workers-XZ6DGH4W.js +0 -23
  57. package/dist/scheduler-NVE6L3P7.js +0 -22
  58. package/dist/store-4HCGBN4L.js +0 -65
@@ -2,18 +2,22 @@ import {
2
2
  computeDiffStats,
3
3
  removeTestWorkspace,
4
4
  syncIssueDiffStatsToStore
5
- } from "./chunk-NB44PCD2.js";
5
+ } from "./chunk-SOBLO4YZ.js";
6
6
  import {
7
- isoWeek,
8
- now
9
- } from "./chunk-2CVTK5F2.js";
7
+ computeMetrics
8
+ } from "./chunk-MVTGAKQK.js";
10
9
  import {
11
- S3DB_ISSUE_RESOURCE,
12
- TERMINAL_STATES
13
- } from "./chunk-37N5OFHM.js";
10
+ markIssueDirty
11
+ } from "./chunk-FJNH3G2Z.js";
14
12
  import {
15
13
  logger
16
14
  } from "./chunk-DVU3CXWA.js";
15
+ import {
16
+ S3DB_ISSUE_RESOURCE,
17
+ TERMINAL_STATES,
18
+ isoWeek,
19
+ now
20
+ } from "./chunk-42AMQAJG.js";
17
21
 
18
22
  // src/agents/failure-analyzer.ts
19
23
  function extractFilePaths(output) {
@@ -170,85 +174,6 @@ ${output}` : "";
170
174
  };
171
175
  }
172
176
 
173
- // src/domains/metrics.ts
174
- function computeMetrics(issues) {
175
- let planning = 0;
176
- let queued = 0;
177
- let inProgress = 0;
178
- let blocked = 0;
179
- let done = 0;
180
- let merged = 0;
181
- let cancelled = 0;
182
- const completionTimes = [];
183
- for (const issue of issues) {
184
- if (issue.state === "Merged") {
185
- const duration = issue.durationMs;
186
- const candidate = typeof duration === "number" && Number.isFinite(duration) ? duration : Number.isFinite(Date.parse(issue.startedAt ?? "")) && Number.isFinite(Date.parse(issue.completedAt ?? "")) ? Date.parse(issue.completedAt) - Date.parse(issue.startedAt) : NaN;
187
- if (Number.isFinite(candidate) && candidate >= 0) {
188
- completionTimes.push(candidate);
189
- }
190
- }
191
- switch (issue.state) {
192
- case "Planning":
193
- planning += 1;
194
- break;
195
- case "PendingApproval":
196
- queued += 1;
197
- break;
198
- case "Queued":
199
- case "Running":
200
- case "Reviewing":
201
- case "PendingDecision":
202
- inProgress += 1;
203
- break;
204
- case "Blocked":
205
- blocked += 1;
206
- break;
207
- case "Approved":
208
- done += 1;
209
- break;
210
- case "Merged":
211
- merged += 1;
212
- break;
213
- case "Cancelled":
214
- cancelled += 1;
215
- break;
216
- }
217
- }
218
- if (completionTimes.length === 0) {
219
- return {
220
- total: issues.length,
221
- planning,
222
- queued,
223
- inProgress,
224
- blocked,
225
- done,
226
- merged,
227
- cancelled,
228
- activeWorkers: 0
229
- };
230
- }
231
- const sortedCompletionTimes = completionTimes.slice().sort((a, b) => a - b);
232
- const totalCompletionMs = sortedCompletionTimes.reduce((acc, value) => acc + value, 0);
233
- const mid = Math.floor(sortedCompletionTimes.length / 2);
234
- const medianCompletionMs = sortedCompletionTimes.length % 2 === 1 ? sortedCompletionTimes[mid] : Math.round((sortedCompletionTimes[mid - 1] + sortedCompletionTimes[mid]) / 2);
235
- return {
236
- total: issues.length,
237
- planning,
238
- queued,
239
- inProgress,
240
- blocked,
241
- done,
242
- merged,
243
- cancelled,
244
- activeWorkers: 0,
245
- avgCompletionMs: Math.round(totalCompletionMs / completionTimes.length),
246
- medianCompletionMs,
247
- fastestCompletionMs: sortedCompletionTimes[0],
248
- slowestCompletionMs: sortedCompletionTimes[sortedCompletionTimes.length - 1]
249
- };
250
- }
251
-
252
177
  // src/persistence/metrics-cache.ts
253
178
  var cachedMetrics = null;
254
179
  var metricsStale = true;
@@ -262,60 +187,57 @@ function getMetrics(issues) {
262
187
  return cachedMetrics;
263
188
  }
264
189
 
265
- // src/persistence/dirty-tracker.ts
266
- var dirtyIssueIds = /* @__PURE__ */ new Set();
267
- var dirtyIssuePlanIds = /* @__PURE__ */ new Set();
268
- var dirtyEventIds = /* @__PURE__ */ new Set();
269
- function markIssueDirty(id) {
270
- dirtyIssueIds.add(id);
271
- }
272
- function markIssuePlanDirty(id) {
273
- dirtyIssuePlanIds.add(id);
274
- }
275
- function markEventDirty(id) {
276
- dirtyEventIds.add(id);
277
- }
278
- function hasDirtyState() {
279
- return dirtyIssueIds.size > 0 || dirtyEventIds.size > 0;
280
- }
281
- function getDirtyIssueIds() {
282
- return dirtyIssueIds;
283
- }
284
- function getDirtyEventIds() {
285
- return dirtyEventIds;
286
- }
287
- function snapshotAndClearDirtyIssueIds() {
288
- const snapshot = new Set(dirtyIssueIds);
289
- for (const id of snapshot) dirtyIssueIds.delete(id);
290
- return snapshot;
291
- }
292
- function snapshotAndClearDirtyIssuePlanIds() {
293
- const snapshot = new Set(dirtyIssuePlanIds);
294
- for (const id of snapshot) dirtyIssuePlanIds.delete(id);
295
- return snapshot;
296
- }
297
- function snapshotAndClearDirtyEventIds() {
298
- const snapshot = new Set(dirtyEventIds);
299
- for (const id of snapshot) dirtyEventIds.delete(id);
300
- return snapshot;
190
+ // src/persistence/plugins/fsm-issue.ts
191
+ import { existsSync, readdirSync, readFileSync, statSync } from "fs";
192
+ import { join } from "path";
193
+
194
+ // src/domains/contract-negotiation.ts
195
+ function requiresContractNegotiation(issue) {
196
+ return issue.plan?.harnessMode === "contractual";
301
197
  }
302
- function markAllIssuesDirty(ids) {
303
- for (const id of ids) dirtyIssueIds.add(id);
198
+ function needsContractNegotiationWork(issue) {
199
+ return requiresContractNegotiation(issue) && issue.contractNegotiationStatus !== "approved" && issue.contractNegotiationStatus !== "failed";
304
200
  }
305
- function markAllIssuePlansDirty(ids) {
306
- for (const id of ids) dirtyIssuePlanIds.add(id);
201
+ function getPlanExecutionBlocker(issue) {
202
+ if (issue.planningStatus === "planning") {
203
+ return `Cannot advance ${issue.identifier} while planning is still running.`;
204
+ }
205
+ if (!issue.plan?.steps?.length) {
206
+ return `Cannot advance ${issue.identifier} because no execution plan is available yet.`;
207
+ }
208
+ if (!requiresContractNegotiation(issue)) return null;
209
+ if (issue.contractNegotiationStatus === "running") {
210
+ return `Cannot advance ${issue.identifier} while contract negotiation is still running.`;
211
+ }
212
+ if (issue.contractNegotiationStatus !== "approved" && issue.contractNegotiationStatus !== "failed") {
213
+ const status = issue.contractNegotiationStatus ?? "pending";
214
+ return `Cannot advance ${issue.identifier} because contractual harness requires approved contract negotiation. Current status: ${status}.`;
215
+ }
216
+ return null;
307
217
  }
308
- function markAllEventsDirty(ids) {
309
- for (const id of ids) dirtyEventIds.add(id);
218
+ function assertPlanReadyForExecution(issue, action) {
219
+ const blocker = getPlanExecutionBlocker(issue);
220
+ if (!blocker) return;
221
+ throw new Error(`${blocker} Refine, replan, or wait for negotiation before trying to ${action}.`);
310
222
  }
311
223
 
312
- // src/persistence/plugins/issue-state-machine.ts
313
- import { existsSync, readdirSync, readFileSync, statSync } from "fs";
314
- import { join } from "path";
224
+ // src/persistence/plugins/fsm-issue.ts
315
225
  var fsmEventEmitter = null;
316
226
  function setFsmEventEmitter(emitter) {
317
227
  fsmEventEmitter = emitter;
318
228
  }
229
+ var persistNowFn = null;
230
+ function setPersistNowFn(fn) {
231
+ persistNowFn = fn;
232
+ }
233
+ function triggerImmediatePersist() {
234
+ if (persistNowFn) {
235
+ try {
236
+ persistNowFn();
237
+ } catch {
238
+ }
239
+ }
240
+ }
319
241
  function cleanupActiveTestWorkspace(issue) {
320
242
  if (!issue.testApplied) return;
321
243
  try {
@@ -333,9 +255,9 @@ function emitFsmEvent(issueId, kind, message) {
333
255
  }
334
256
  }
335
257
  if (kind === "state") {
336
- import("./web-push-CRVDJKWR.js").then(({ sendPushToAll, isWebPushReady, SETTING_ID_PUSH_SUBSCRIPTIONS }) => {
258
+ import("./web-push-AX5IIK3P.js").then(({ sendPushToAll, isWebPushReady, SETTING_ID_PUSH_SUBSCRIPTIONS }) => {
337
259
  if (!isWebPushReady()) return;
338
- import("./settings-NGY33WQE.js").then(({ persistSetting }) => {
260
+ import("./settings-ZAWDCFP2.js").then(({ persistSetting }) => {
339
261
  sendPushToAll(
340
262
  { title: "fifony", body: message, tag: issueId, url: "/kanban" },
341
263
  async (subs) => {
@@ -391,10 +313,12 @@ var issueStateMachineConfig = {
391
313
  states: {
392
314
  Planning: {
393
315
  on: { PLANNED: "PendingApproval", CANCEL: "Cancelled" },
316
+ guards: { PLANNED: "requireReadyExecutionPlan" },
394
317
  entry: "onEnterPlanning"
395
318
  },
396
319
  PendingApproval: {
397
320
  on: { QUEUE: "Queued", REPLAN: "Planning", CANCEL: "Cancelled" },
321
+ guards: { QUEUE: "requireReadyExecutionPlan" },
398
322
  entry: "onEnterPendingApproval"
399
323
  },
400
324
  Queued: {
@@ -468,6 +392,12 @@ var issueStateMachineConfig = {
468
392
  issue.planningError = void 0;
469
393
  issue.nextRetryAt = void 0;
470
394
  issue.lastError = void 0;
395
+ issue.checkpointAttempt = 0;
396
+ issue.checkpointStatus = void 0;
397
+ issue.checkpointPassedAt = void 0;
398
+ issue.checkpointReport = void 0;
399
+ issue.contractNegotiationAttempt = 0;
400
+ issue.contractNegotiationStatus = void 0;
471
401
  emitFsmEvent(issue.id, "state", `${issue.identifier} entered Planning.`);
472
402
  lazyEnqueue(issue, "plan").catch(() => {
473
403
  });
@@ -550,6 +480,9 @@ var issueStateMachineConfig = {
550
480
  issue.nextRetryAt = void 0;
551
481
  issue.lastError = void 0;
552
482
  issue.lastFailedPhase = void 0;
483
+ issue.checkpointStatus = void 0;
484
+ issue.checkpointPassedAt = void 0;
485
+ issue.checkpointReport = void 0;
553
486
  logger.info({ issueId: issue.id, identifier: issue.identifier }, "[FSM] onEnterQueued \u2014 enqueuing for execution");
554
487
  emitFsmEvent(issue.id, "state", `${issue.identifier} queued for execution.`);
555
488
  lazyEnqueue(issue, "execute").catch((err) => {
@@ -663,6 +596,11 @@ var issueStateMachineConfig = {
663
596
  guards: {
664
597
  requireBlockReason: async (context, _event, _machine) => {
665
598
  return typeof context.note === "string" && context.note.trim().length > 0;
599
+ },
600
+ requireReadyExecutionPlan: async (context, _event, _machine) => {
601
+ const issue = resolveIssue(context);
602
+ if (!issue) return false;
603
+ return getPlanExecutionBlocker(issue) === null;
666
604
  }
667
605
  }
668
606
  };
@@ -823,6 +761,7 @@ async function executeTransition(issue, event, context = {}) {
823
761
  issue.terminalWeek = "";
824
762
  }
825
763
  markDirtyAndInvalidate(issue.id);
764
+ triggerImmediatePersist();
826
765
  return { previousState: previous };
827
766
  }
828
767
  async function getIssueTransitionHistory(issueId, options) {
@@ -859,21 +798,14 @@ function visualizeStateMachine() {
859
798
  }
860
799
 
861
800
  export {
862
- computeMetrics,
863
801
  getMetrics,
864
- markIssueDirty,
865
- markIssuePlanDirty,
866
- markEventDirty,
867
- hasDirtyState,
868
- getDirtyIssueIds,
869
- getDirtyEventIds,
870
- snapshotAndClearDirtyIssueIds,
871
- snapshotAndClearDirtyIssuePlanIds,
872
- snapshotAndClearDirtyEventIds,
873
- markAllIssuesDirty,
874
- markAllIssuePlansDirty,
875
- markAllEventsDirty,
802
+ requiresContractNegotiation,
803
+ needsContractNegotiationWork,
804
+ getPlanExecutionBlocker,
805
+ assertPlanReadyForExecution,
806
+ extractFailureInsights,
876
807
  setFsmEventEmitter,
808
+ setPersistNowFn,
877
809
  setEnqueueFn,
878
810
  ISSUE_STATE_MACHINE_ID,
879
811
  issueStateMachineConfig,
@@ -891,4 +823,4 @@ export {
891
823
  canTransitionIssue,
892
824
  visualizeStateMachine
893
825
  };
894
- //# sourceMappingURL=chunk-H5N7O5NP.js.map
826
+ //# sourceMappingURL=chunk-AILXZ2TD.js.map
@@ -63,7 +63,7 @@ async function sendPushToAll(payload, persistSubscriptions) {
63
63
  const jsonPayload = JSON.stringify(payload);
64
64
  let sent = 0;
65
65
  let removed = 0;
66
- const results = await Promise.allSettled(
66
+ await Promise.allSettled(
67
67
  [...subscriptions.values()].map(async (sub) => {
68
68
  try {
69
69
  await webpush.sendNotification(sub, jsonPayload, { TTL: 3600 });
@@ -101,4 +101,4 @@ export {
101
101
  getSubscriptionCount,
102
102
  sendPushToAll
103
103
  };
104
- //# sourceMappingURL=chunk-I2UHVKHS.js.map
104
+ //# sourceMappingURL=chunk-BRSR26VK.js.map