@generacy-ai/generacy 0.5.0 → 0.6.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 (87) hide show
  1. package/dist/cli/commands/cockpit/clarification-answer-marker.d.ts +8 -0
  2. package/dist/cli/commands/cockpit/clarification-answer-marker.d.ts.map +1 -0
  3. package/dist/cli/commands/cockpit/clarification-answer-marker.js +76 -0
  4. package/dist/cli/commands/cockpit/clarification-answer-marker.js.map +1 -0
  5. package/dist/cli/commands/cockpit/clarification-comment-finder.d.ts.map +1 -1
  6. package/dist/cli/commands/cockpit/clarification-comment-finder.js +33 -3
  7. package/dist/cli/commands/cockpit/clarification-comment-finder.js.map +1 -1
  8. package/dist/cli/commands/cockpit/clarify-relay.d.ts +38 -0
  9. package/dist/cli/commands/cockpit/clarify-relay.d.ts.map +1 -0
  10. package/dist/cli/commands/cockpit/clarify-relay.js +120 -0
  11. package/dist/cli/commands/cockpit/clarify-relay.js.map +1 -0
  12. package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.d.ts +43 -0
  13. package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.d.ts.map +1 -0
  14. package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.js +72 -0
  15. package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.js.map +1 -0
  16. package/dist/cli/commands/cockpit/doorbell/channel-discovery.d.ts +37 -0
  17. package/dist/cli/commands/cockpit/doorbell/channel-discovery.d.ts.map +1 -0
  18. package/dist/cli/commands/cockpit/doorbell/channel-discovery.js +92 -0
  19. package/dist/cli/commands/cockpit/doorbell/channel-discovery.js.map +1 -0
  20. package/dist/cli/commands/cockpit/doorbell/smee-source.d.ts +73 -0
  21. package/dist/cli/commands/cockpit/doorbell/smee-source.d.ts.map +1 -0
  22. package/dist/cli/commands/cockpit/doorbell/smee-source.js +362 -0
  23. package/dist/cli/commands/cockpit/doorbell/smee-source.js.map +1 -0
  24. package/dist/cli/commands/cockpit/doorbell/source-selector.d.ts +56 -0
  25. package/dist/cli/commands/cockpit/doorbell/source-selector.d.ts.map +1 -0
  26. package/dist/cli/commands/cockpit/doorbell/source-selector.js +182 -0
  27. package/dist/cli/commands/cockpit/doorbell/source-selector.js.map +1 -0
  28. package/dist/cli/commands/cockpit/doorbell/sse-parser.d.ts +15 -0
  29. package/dist/cli/commands/cockpit/doorbell/sse-parser.d.ts.map +1 -0
  30. package/dist/cli/commands/cockpit/doorbell/sse-parser.js +43 -0
  31. package/dist/cli/commands/cockpit/doorbell/sse-parser.js.map +1 -0
  32. package/dist/cli/commands/cockpit/doorbell/startup-retry.d.ts +55 -0
  33. package/dist/cli/commands/cockpit/doorbell/startup-retry.d.ts.map +1 -0
  34. package/dist/cli/commands/cockpit/doorbell/startup-retry.js +159 -0
  35. package/dist/cli/commands/cockpit/doorbell/startup-retry.js.map +1 -0
  36. package/dist/cli/commands/cockpit/doorbell/subscribe.d.ts +19 -0
  37. package/dist/cli/commands/cockpit/doorbell/subscribe.d.ts.map +1 -0
  38. package/dist/cli/commands/cockpit/doorbell/subscribe.js +54 -0
  39. package/dist/cli/commands/cockpit/doorbell/subscribe.js.map +1 -0
  40. package/dist/cli/commands/cockpit/doorbell/webhook-to-event.d.ts +28 -0
  41. package/dist/cli/commands/cockpit/doorbell/webhook-to-event.d.ts.map +1 -0
  42. package/dist/cli/commands/cockpit/doorbell/webhook-to-event.js +159 -0
  43. package/dist/cli/commands/cockpit/doorbell/webhook-to-event.js.map +1 -0
  44. package/dist/cli/commands/cockpit/doorbell.d.ts +58 -0
  45. package/dist/cli/commands/cockpit/doorbell.d.ts.map +1 -0
  46. package/dist/cli/commands/cockpit/doorbell.js +477 -0
  47. package/dist/cli/commands/cockpit/doorbell.js.map +1 -0
  48. package/dist/cli/commands/cockpit/index.d.ts +1 -0
  49. package/dist/cli/commands/cockpit/index.d.ts.map +1 -1
  50. package/dist/cli/commands/cockpit/index.js +3 -0
  51. package/dist/cli/commands/cockpit/index.js.map +1 -1
  52. package/dist/cli/commands/cockpit/mcp/event-bus-registry.d.ts +2 -1
  53. package/dist/cli/commands/cockpit/mcp/event-bus-registry.d.ts.map +1 -1
  54. package/dist/cli/commands/cockpit/mcp/event-bus-registry.js +38 -9
  55. package/dist/cli/commands/cockpit/mcp/event-bus-registry.js.map +1 -1
  56. package/dist/cli/commands/cockpit/mcp/schemas.d.ts +48 -0
  57. package/dist/cli/commands/cockpit/mcp/schemas.d.ts.map +1 -1
  58. package/dist/cli/commands/cockpit/mcp/schemas.js +28 -0
  59. package/dist/cli/commands/cockpit/mcp/schemas.js.map +1 -1
  60. package/dist/cli/commands/cockpit/mcp/server.d.ts.map +1 -1
  61. package/dist/cli/commands/cockpit/mcp/server.js +6 -1
  62. package/dist/cli/commands/cockpit/mcp/server.js.map +1 -1
  63. package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.d.ts +45 -0
  64. package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.d.ts.map +1 -0
  65. package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.js +45 -0
  66. package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.js.map +1 -0
  67. package/dist/cli/commands/cockpit/resume.d.ts +12 -6
  68. package/dist/cli/commands/cockpit/resume.d.ts.map +1 -1
  69. package/dist/cli/commands/cockpit/resume.js +27 -11
  70. package/dist/cli/commands/cockpit/resume.js.map +1 -1
  71. package/dist/cli/commands/cockpit/watch/poll-loop.d.ts +2 -0
  72. package/dist/cli/commands/cockpit/watch/poll-loop.d.ts.map +1 -1
  73. package/dist/cli/commands/cockpit/watch/poll-loop.js +38 -6
  74. package/dist/cli/commands/cockpit/watch/poll-loop.js.map +1 -1
  75. package/dist/cli/commands/cockpit/watch/pr-state.d.ts +15 -1
  76. package/dist/cli/commands/cockpit/watch/pr-state.d.ts.map +1 -1
  77. package/dist/cli/commands/cockpit/watch/pr-state.js +38 -0
  78. package/dist/cli/commands/cockpit/watch/pr-state.js.map +1 -1
  79. package/dist/cli/commands/cockpit/watch/snapshot.d.ts +6 -1
  80. package/dist/cli/commands/cockpit/watch/snapshot.d.ts.map +1 -1
  81. package/dist/cli/commands/cockpit/watch/snapshot.js +3 -1
  82. package/dist/cli/commands/cockpit/watch/snapshot.js.map +1 -1
  83. package/dist/cli/commands/cockpit/watch.d.ts +2 -1
  84. package/dist/cli/commands/cockpit/watch.d.ts.map +1 -1
  85. package/dist/cli/commands/cockpit/watch.js +25 -13
  86. package/dist/cli/commands/cockpit/watch.js.map +1 -1
  87. package/package.json +5 -5
@@ -0,0 +1,8 @@
1
+ export interface ClarificationAnswerMarker {
2
+ batch: number;
3
+ answers: Record<number, string>;
4
+ actor?: string;
5
+ ts: string;
6
+ }
7
+ export declare function formatClarificationAnswerComment(marker: ClarificationAnswerMarker): string;
8
+ //# sourceMappingURL=clarification-answer-marker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarification-answer-marker.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-answer-marker.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,yBAAyB,GAChC,MAAM,CAmBR"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * #958 — `formatClarificationAnswerComment` renders the marker-stamped answer
3
+ * comment posted by `cockpit_relay_clarify_answers`. Mirrors the deterministic
4
+ * validation pattern in `manual-advance-marker.ts`.
5
+ *
6
+ * The header line matches `commentCarriesAnswerMarker` in
7
+ * `packages/orchestrator/src/worker/clarification-markers.ts` — this is the
8
+ * sole writer of the `<!-- generacy-clarification-answers:<batch> -->`
9
+ * prefix. Agents never free-write it.
10
+ *
11
+ * Inputs are regex-validated before interpolation so a malformed `batch`,
12
+ * `actor`, or `ts` cannot inject markdown/HTML into the marker body.
13
+ */
14
+ import { PENDING_ANSWER_LITERAL } from '@generacy-ai/workflow-engine';
15
+ const ACTOR_REGEX = /^[A-Za-z0-9-]+$/;
16
+ export function formatClarificationAnswerComment(marker) {
17
+ validate(marker);
18
+ const { batch, answers, actor, ts } = marker;
19
+ const hasActor = typeof actor === 'string' && actor.length > 0;
20
+ const header = hasActor
21
+ ? `<!-- generacy-clarification-answers:${batch} actor=${actor} ts=${ts} -->`
22
+ : `<!-- generacy-clarification-answers:${batch} ts=${ts} -->`;
23
+ const orderedKeys = Object.keys(answers)
24
+ .map((k) => Number(k))
25
+ .sort((a, b) => a - b);
26
+ const answerLines = orderedKeys.map((n) => `Q${n}: ${answers[n]}`);
27
+ return [
28
+ header,
29
+ '',
30
+ `## Answers — batch ${batch}`,
31
+ '',
32
+ ...answerLines,
33
+ '',
34
+ ].join('\n');
35
+ }
36
+ function validate(marker) {
37
+ if (!Number.isInteger(marker.batch) || marker.batch < 0) {
38
+ throw new Error(`formatClarificationAnswerComment: batch must be a non-negative integer (got ${String(marker.batch)})`);
39
+ }
40
+ if (typeof marker.actor === 'string' && marker.actor.length > 0) {
41
+ if (!ACTOR_REGEX.test(marker.actor)) {
42
+ throw new Error(`formatClarificationAnswerComment: invalid actor login "${marker.actor}"`);
43
+ }
44
+ }
45
+ if (typeof marker.ts !== 'string' || marker.ts === '') {
46
+ throw new Error('formatClarificationAnswerComment: ts must be a non-empty ISO-8601 string');
47
+ }
48
+ const parsed = new Date(marker.ts);
49
+ if (Number.isNaN(parsed.getTime()) || parsed.toISOString() !== marker.ts) {
50
+ throw new Error(`formatClarificationAnswerComment: ts "${marker.ts}" is not round-trip ISO-8601`);
51
+ }
52
+ if (marker.answers == null || typeof marker.answers !== 'object') {
53
+ throw new Error('formatClarificationAnswerComment: answers must be a Record<number, string>');
54
+ }
55
+ const keys = Object.keys(marker.answers);
56
+ if (keys.length === 0) {
57
+ throw new Error('formatClarificationAnswerComment: answers map is empty');
58
+ }
59
+ for (const key of keys) {
60
+ const n = Number(key);
61
+ if (!Number.isInteger(n) || n <= 0) {
62
+ throw new Error(`formatClarificationAnswerComment: answer key "${key}" is not a positive integer`);
63
+ }
64
+ const value = marker.answers[n];
65
+ if (typeof value !== 'string' || value.length === 0) {
66
+ throw new Error(`formatClarificationAnswerComment: answer for Q${n} is empty`);
67
+ }
68
+ }
69
+ // Reference the shared pending literal so the import stays live (per
70
+ // data-model.md §"Cross-package import strategy" — divergence between the
71
+ // parser's canonical value and cockpit's rendered surface is structurally
72
+ // impossible). The stamped body never renders the literal itself; the tool
73
+ // refuses empty values above.
74
+ void PENDING_ANSWER_LITERAL;
75
+ }
76
+ //# sourceMappingURL=clarification-answer-marker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarification-answer-marker.js","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-answer-marker.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"clarification-comment-finder.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-comment-finder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAUpE,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA0B9B"}
1
+ {"version":3,"file":"clarification-comment-finder.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-comment-finder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAuCpE,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA0B9B"}
@@ -1,4 +1,32 @@
1
1
  const WAITING_CLARIFICATION = 'waiting-for:clarification';
2
+ const STAGE_STATUS_REJECT_PREFIXES = [
3
+ '<!-- generacy-stage:planning',
4
+ '<!-- generacy-stage:specification',
5
+ '<!-- generacy-stage:implementation',
6
+ '<!-- speckit-stage:planning',
7
+ '<!-- speckit-stage:specification',
8
+ '<!-- speckit-stage:implementation',
9
+ ];
10
+ const CLARIFICATION_STAGE_OVERRIDE_PREFIXES = [
11
+ '<!-- generacy-stage:clarification',
12
+ '<!-- generacy-stage:clarification-batch-',
13
+ ];
14
+ function isStageStatusComment(body) {
15
+ const lines = body.split('\n');
16
+ for (const line of lines) {
17
+ for (const prefix of CLARIFICATION_STAGE_OVERRIDE_PREFIXES) {
18
+ if (line.startsWith(prefix))
19
+ return false;
20
+ }
21
+ }
22
+ for (const line of lines) {
23
+ for (const prefix of STAGE_STATUS_REJECT_PREFIXES) {
24
+ if (line.startsWith(prefix))
25
+ return true;
26
+ }
27
+ }
28
+ return false;
29
+ }
2
30
  export async function findClarificationComment(gh, repo, number) {
3
31
  const timeline = (await gh.fetchIssueTimeline(repo, number));
4
32
  let latestLabelTs = null;
@@ -22,9 +50,11 @@ export async function findClarificationComment(gh, repo, number) {
22
50
  const sorted = [...comments].sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt));
23
51
  for (const c of sorted) {
24
52
  const ct = Date.parse(c.createdAt);
25
- if (!Number.isNaN(ct) && ct >= labelTime) {
26
- return c;
27
- }
53
+ if (Number.isNaN(ct) || ct < labelTime)
54
+ continue;
55
+ if (isStageStatusComment(c.body))
56
+ continue;
57
+ return c;
28
58
  }
29
59
  return null;
30
60
  }
@@ -1 +1 @@
1
- {"version":3,"file":"clarification-comment-finder.js","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-comment-finder.ts"],"names":[],"mappings":"AAYA,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAQ1D,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,EAAa,EACb,IAAY,EACZ,MAAc;IAEd,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAyB,CAAC;IAErF,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,SAAS;QACxC,IAAI,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,qBAAqB;YAAE,SAAS;QAC1D,IAAI,KAAK,CAAC,UAAU,IAAI,IAAI;YAAE,SAAS;QACvC,IAAI,aAAa,IAAI,IAAI,IAAI,KAAK,CAAC,UAAU,GAAG,aAAa,EAAE,CAAC;YAC9D,aAAa,GAAG,KAAK,CAAC,UAAU,CAAC;QACnC,CAAC;IACH,CAAC;IAED,IAAI,aAAa,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC5C,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/F,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,SAAS,EAAE,CAAC;YACzC,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"clarification-comment-finder.js","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-comment-finder.ts"],"names":[],"mappings":"AAYA,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAE1D,MAAM,4BAA4B,GAAsB;IACtD,8BAA8B;IAC9B,mCAAmC;IACnC,oCAAoC;IACpC,6BAA6B;IAC7B,kCAAkC;IAClC,mCAAmC;CAC3B,CAAC;AAEX,MAAM,qCAAqC,GAAsB;IAC/D,mCAAmC;IACnC,0CAA0C;CAClC,CAAC;AAEX,SAAS,oBAAoB,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,MAAM,IAAI,qCAAqC,EAAE,CAAC;YAC3D,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO,KAAK,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,MAAM,IAAI,4BAA4B,EAAE,CAAC;YAClD,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC3C,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,EAAa,EACb,IAAY,EACZ,MAAc;IAEd,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAyB,CAAC;IAErF,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,SAAS;QACxC,IAAI,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,qBAAqB;YAAE,SAAS;QAC1D,IAAI,KAAK,CAAC,UAAU,IAAI,IAAI;YAAE,SAAS;QACvC,IAAI,aAAa,IAAI,IAAI,IAAI,KAAK,CAAC,UAAU,GAAG,aAAa,EAAE,CAAC;YAC9D,aAAa,GAAG,KAAK,CAAC,UAAU,CAAC;QACnC,CAAC;IACH,CAAC;IAED,IAAI,aAAa,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC5C,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/F,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,SAAS;YAAE,SAAS;QACjD,IAAI,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,SAAS;QAC3C,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * #958 — `runClarifyRelay` — deterministic answer-relay path for cockpit's
3
+ * clarify skill. Replaces the freehand `gh issue comment` invocation that
4
+ * caused #958's answer-side lottery (four different agent-invented markers
5
+ * observed on #5/#6/#7/#8 in one run).
6
+ *
7
+ * The tool posts a marker-stamped comment (via `formatClarificationAnswerComment`)
8
+ * and applies `completed:clarification`. Idempotent: a second call with the
9
+ * same batch is a no-op (`action: 'already-relayed'`).
10
+ *
11
+ * MUST NOT rewrite `clarifications.md` locally. MUST NOT remove
12
+ * `waiting-for:clarification` (the worker owns that on resume).
13
+ */
14
+ import { loadCockpitConfig, type CommandRunner, type GhWrapper } from '@generacy-ai/cockpit';
15
+ import { type IssueRef } from './resolver.js';
16
+ export interface ClarifyRelayInput {
17
+ issue: string;
18
+ batch: number;
19
+ answers: Record<number, string>;
20
+ actor?: string;
21
+ }
22
+ export interface ClarifyRelayResult {
23
+ ref: IssueRef;
24
+ batch: number;
25
+ action: 'relayed' | 'already-relayed';
26
+ completedLabel: 'completed:clarification';
27
+ commentUrl?: string;
28
+ noop?: true;
29
+ }
30
+ export interface ClarifyRelayDeps {
31
+ runner?: CommandRunner;
32
+ gh?: GhWrapper;
33
+ loadConfig?: typeof loadCockpitConfig;
34
+ env?: NodeJS.ProcessEnv;
35
+ now?: () => Date;
36
+ }
37
+ export declare function runClarifyRelay(input: ClarifyRelayInput, deps?: ClarifyRelayDeps): Promise<ClarifyRelayResult>;
38
+ //# sourceMappingURL=clarify-relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarify-relay.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarify-relay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAuB,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAKnE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,iBAAiB,CAAC;IACtC,cAAc,EAAE,yBAAyB,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,iBAAiB,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAiBD,wBAAsB,eAAe,CACnC,KAAK,EAAE,iBAAiB,EACxB,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,kBAAkB,CAAC,CAkG7B"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * #958 — `runClarifyRelay` — deterministic answer-relay path for cockpit's
3
+ * clarify skill. Replaces the freehand `gh issue comment` invocation that
4
+ * caused #958's answer-side lottery (four different agent-invented markers
5
+ * observed on #5/#6/#7/#8 in one run).
6
+ *
7
+ * The tool posts a marker-stamped comment (via `formatClarificationAnswerComment`)
8
+ * and applies `completed:clarification`. Idempotent: a second call with the
9
+ * same batch is a no-op (`action: 'already-relayed'`).
10
+ *
11
+ * MUST NOT rewrite `clarifications.md` locally. MUST NOT remove
12
+ * `waiting-for:clarification` (the worker owns that on resume).
13
+ */
14
+ import { loadCockpitConfig, } from '@generacy-ai/cockpit';
15
+ import { getLogger } from '../../utils/logger.js';
16
+ import { resolveIssueContext } from './resolver.js';
17
+ import { CockpitExit } from './exit.js';
18
+ import { resolveCockpitIdentity } from './shared/identity.js';
19
+ import { formatClarificationAnswerComment } from './clarification-answer-marker.js';
20
+ const COMPLETED_LABEL = 'completed:clarification';
21
+ /**
22
+ * Detect a prior stamped answer comment for the same batch. The marker header
23
+ * carries the batch as the first attribute (`:<batch>`) — any comment whose
24
+ * body starts a line with `<!-- generacy-clarification-answers:<batch>` matches.
25
+ */
26
+ function hasPriorMarkerForBatch(body, batch) {
27
+ const prefix = `<!-- generacy-clarification-answers:${batch}`;
28
+ for (const line of body.split('\n')) {
29
+ if (line.startsWith(prefix))
30
+ return true;
31
+ }
32
+ return false;
33
+ }
34
+ export async function runClarifyRelay(input, deps = {}) {
35
+ const log = getLogger();
36
+ const loaded = await (deps.loadConfig ?? loadCockpitConfig)({});
37
+ for (const w of loaded.warnings)
38
+ log.warn(w);
39
+ let ref;
40
+ let gh;
41
+ try {
42
+ const resolvedCtx = await resolveIssueContext({
43
+ issue: input.issue,
44
+ ...(deps.runner != null ? { runner: deps.runner } : {}),
45
+ });
46
+ ref = resolvedCtx.ref;
47
+ gh = deps.gh ?? resolvedCtx.gh;
48
+ }
49
+ catch (err) {
50
+ throw new CockpitExit(2, `Error: cockpit clarify-relay: ${err.message}`);
51
+ }
52
+ // Resolve actor (optional). An unresolvable identity leaves the actor attr
53
+ // off the marker header — the marker match rule is prefix-based so this is
54
+ // a cosmetic degradation, not a functional one.
55
+ const identityInput = {
56
+ flag: undefined,
57
+ configAssignee: loaded.config.assignee,
58
+ gh,
59
+ logger: log,
60
+ verb: 'clarify-relay',
61
+ mode: 'optional',
62
+ ...(deps.env != null ? { env: deps.env } : {}),
63
+ };
64
+ const identity = await resolveCockpitIdentity(identityInput);
65
+ const actor = input.actor ?? identity.login;
66
+ // Idempotence — a comment carrying this batch's marker means we already
67
+ // relayed. Return no-op without re-posting or re-labelling.
68
+ let existingComments;
69
+ try {
70
+ existingComments = await gh.fetchIssueComments(ref.nwo, ref.number);
71
+ }
72
+ catch (err) {
73
+ throw new CockpitExit(1, `Error: cockpit clarify-relay: gh issue view (fetch comments): ${err.message}`);
74
+ }
75
+ const alreadyRelayed = existingComments.some((c) => hasPriorMarkerForBatch(c.body, input.batch));
76
+ if (alreadyRelayed) {
77
+ return {
78
+ ref,
79
+ batch: input.batch,
80
+ action: 'already-relayed',
81
+ completedLabel: COMPLETED_LABEL,
82
+ noop: true,
83
+ };
84
+ }
85
+ const ts = (deps.now ?? (() => new Date()))().toISOString();
86
+ let body;
87
+ try {
88
+ body = formatClarificationAnswerComment({
89
+ batch: input.batch,
90
+ answers: input.answers,
91
+ ...(actor != null ? { actor } : {}),
92
+ ts,
93
+ });
94
+ }
95
+ catch (err) {
96
+ throw new CockpitExit(2, `Error: cockpit clarify-relay: ${err.message}`);
97
+ }
98
+ let commentUrl;
99
+ try {
100
+ commentUrl = (await gh.postIssueComment(ref.nwo, ref.number, body)).url;
101
+ }
102
+ catch (err) {
103
+ throw new CockpitExit(1, `Error: cockpit clarify-relay: gh issue comment: ${err.message}`);
104
+ }
105
+ try {
106
+ await gh.addLabel(ref.nwo, ref.number, COMPLETED_LABEL);
107
+ }
108
+ catch (err) {
109
+ throw new CockpitExit(1, `Error: cockpit clarify-relay: gh issue edit (add ${COMPLETED_LABEL}): ${err.message}` +
110
+ ` (comment posted: ${commentUrl})`);
111
+ }
112
+ return {
113
+ ref,
114
+ batch: input.batch,
115
+ action: 'relayed',
116
+ completedLabel: COMPLETED_LABEL,
117
+ commentUrl,
118
+ };
119
+ }
120
+ //# sourceMappingURL=clarify-relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarify-relay.js","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarify-relay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,iBAAiB,GAGlB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAiB,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,gCAAgC,EAAE,MAAM,kCAAkC,CAAC;AA0BpF,MAAM,eAAe,GAAG,yBAAkC,CAAC;AAE3D;;;;GAIG;AACH,SAAS,sBAAsB,CAAC,IAAY,EAAE,KAAa;IACzD,MAAM,MAAM,GAAG,uCAAuC,KAAK,EAAE,CAAC;IAC9D,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;IAC3C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAwB,EACxB,OAAyB,EAAE;IAE3B,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IAExB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC;IAChE,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ;QAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE7C,IAAI,GAAa,CAAC;IAClB,IAAI,EAAa,CAAC;IAClB,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC;YAC5C,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxD,CAAC,CAAC;QACH,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC;QACtB,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,WAAW,CAAC,EAAE,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CAAC,CAAC,EAAE,iCAAkC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,2EAA2E;IAC3E,2EAA2E;IAC3E,gDAAgD;IAChD,MAAM,aAAa,GAAiD;QAClE,IAAI,EAAE,SAAS;QACf,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ;QACtC,EAAE;QACF,MAAM,EAAE,GAAG;QACX,IAAI,EAAE,eAAe;QACrB,IAAI,EAAE,UAAU;QAChB,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/C,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC,aAAa,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC;IAE5C,wEAAwE;IACxE,4DAA4D;IAC5D,IAAI,gBAAgB,CAAC;IACrB,IAAI,CAAC;QACH,gBAAgB,GAAG,MAAM,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CACnB,CAAC,EACD,iEAAkE,GAAa,CAAC,OAAO,EAAE,CAC1F,CAAC;IACJ,CAAC;IACD,MAAM,cAAc,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACjD,sBAAsB,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAC5C,CAAC;IACF,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO;YACL,GAAG;YACH,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,iBAAiB;YACzB,cAAc,EAAE,eAAe;YAC/B,IAAI,EAAE,IAAI;SACX,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5D,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,gCAAgC,CAAC;YACtC,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnC,EAAE;SACH,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CAAC,CAAC,EAAE,iCAAkC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,UAAU,GAAG,CAAC,MAAM,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;IAC1E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CACnB,CAAC,EACD,mDAAoD,GAAa,CAAC,OAAO,EAAE,CAC5E,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC1D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CACnB,CAAC,EACD,oDAAoD,eAAe,MAAO,GAAa,CAAC,OAAO,EAAE;YAC/F,qBAAqB,UAAU,GAAG,CACrC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,GAAG;QACH,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE,SAAS;QACjB,cAAc,EAAE,eAAe;QAC/B,UAAU;KACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `maybeRefreshAggregate` — Q4=A on-demand aggregate refresh. Recomputes
3
+ * `phase-complete` / `epic-complete` events only when the current webhook
4
+ * payload could plausibly change an aggregate state (completed:* label,
5
+ * issue.closed, pull_request.closed). Zero GraphQL cost on non-completion
6
+ * payloads.
7
+ *
8
+ * Contract: `specs/978-summary-generacy-cockpit/contracts/aggregate-on-demand.md`.
9
+ */
10
+ import { type GhWrapper, type ResolvedEpic } from '@generacy-ai/cockpit';
11
+ import { type AggregateState } from '../watch/aggregate.js';
12
+ import type { AggregateEvent } from '../watch/aggregate-emit.js';
13
+ import type { SnapshotMap } from '../watch/snapshot.js';
14
+ export type AggregateTrigger = {
15
+ kind: 'completed-label';
16
+ label: string;
17
+ } | {
18
+ kind: 'issue-closed';
19
+ } | {
20
+ kind: 'pr-closed';
21
+ } | null;
22
+ export interface AggregateRefreshInput {
23
+ trigger: AggregateTrigger;
24
+ epicRef: string;
25
+ epicRepo: string;
26
+ epicNumber: number;
27
+ prevAgg: AggregateState;
28
+ prev: SnapshotMap;
29
+ currentResolved: ResolvedEpic | null;
30
+ gh: GhWrapper;
31
+ logger: {
32
+ warn: (msg: string) => void;
33
+ };
34
+ now: () => string;
35
+ }
36
+ export interface AggregateRefreshOutput {
37
+ events: AggregateEvent[];
38
+ nextAgg: AggregateState;
39
+ nextPrev: SnapshotMap;
40
+ nextResolved: ResolvedEpic | null;
41
+ }
42
+ export declare function maybeRefreshAggregate(input: AggregateRefreshInput): Promise<AggregateRefreshOutput>;
43
+ //# sourceMappingURL=aggregate-on-demand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate-on-demand.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/aggregate-on-demand.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAe,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEtF,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB,IAAI,CAAC;AAET,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,WAAW,CAAC;IAClB,eAAe,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IACxC,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC;AAWD,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,CAAC,CA2DjC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * `maybeRefreshAggregate` — Q4=A on-demand aggregate refresh. Recomputes
3
+ * `phase-complete` / `epic-complete` events only when the current webhook
4
+ * payload could plausibly change an aggregate state (completed:* label,
5
+ * issue.closed, pull_request.closed). Zero GraphQL cost on non-completion
6
+ * payloads.
7
+ *
8
+ * Contract: `specs/978-summary-generacy-cockpit/contracts/aggregate-on-demand.md`.
9
+ */
10
+ import { resolveEpic } from '@generacy-ai/cockpit';
11
+ import { runOnePoll } from '../watch/poll-loop.js';
12
+ import { computeAggregateEvents } from '../watch/aggregate.js';
13
+ function identity(input) {
14
+ return {
15
+ events: [],
16
+ nextAgg: input.prevAgg,
17
+ nextPrev: input.prev,
18
+ nextResolved: input.currentResolved,
19
+ };
20
+ }
21
+ export async function maybeRefreshAggregate(input) {
22
+ if (input.trigger == null)
23
+ return identity(input);
24
+ let resolved = input.currentResolved;
25
+ if (resolved == null) {
26
+ try {
27
+ resolved = await resolveEpic({
28
+ epicRef: input.epicRef,
29
+ gh: input.gh,
30
+ logger: input.logger,
31
+ });
32
+ }
33
+ catch (err) {
34
+ input.logger.warn(`cockpit doorbell: aggregate resolveEpic failed: ${err instanceof Error ? err.message : String(err)}`);
35
+ return identity(input);
36
+ }
37
+ }
38
+ let pollResult;
39
+ try {
40
+ pollResult = await runOnePoll(input.prev, {
41
+ gh: input.gh,
42
+ refs: resolved.parsed.allRefs,
43
+ epicOwnerRepo: resolved.epic.repo,
44
+ logger: input.logger,
45
+ });
46
+ }
47
+ catch (err) {
48
+ input.logger.warn(`cockpit doorbell: aggregate runOnePoll failed: ${err instanceof Error ? err.message : String(err)}`);
49
+ return {
50
+ events: [],
51
+ nextAgg: input.prevAgg,
52
+ nextPrev: input.prev,
53
+ nextResolved: resolved,
54
+ };
55
+ }
56
+ const aggregate = computeAggregateEvents({
57
+ curr: pollResult.curr,
58
+ parsed: resolved.parsed,
59
+ epicRepo: input.epicRepo,
60
+ epicNumber: input.epicNumber,
61
+ prevState: input.prevAgg,
62
+ initial: false,
63
+ now: input.now,
64
+ });
65
+ return {
66
+ events: aggregate.events,
67
+ nextAgg: aggregate.nextState,
68
+ nextPrev: pollResult.curr,
69
+ nextResolved: resolved,
70
+ };
71
+ }
72
+ //# sourceMappingURL=aggregate-on-demand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate-on-demand.js","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/aggregate-on-demand.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,WAAW,EAAqC,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAuB,MAAM,uBAAuB,CAAC;AA8BpF,SAAS,QAAQ,CAAC,KAA4B;IAC5C,OAAO;QACL,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,QAAQ,EAAE,KAAK,CAAC,IAAI;QACpB,YAAY,EAAE,KAAK,CAAC,eAAe;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAA4B;IAE5B,IAAI,KAAK,CAAC,OAAO,IAAI,IAAI;QAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;IAElD,IAAI,QAAQ,GAAwB,KAAK,CAAC,eAAe,CAAC;IAC1D,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,WAAW,CAAC;gBAC3B,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,MAAM,EAAE,KAAK,CAAC,MAAM;aACrB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,MAAM,CAAC,IAAI,CACf,mDACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;YACF,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,IAAI,UAAiC,CAAC;IACtC,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE;YACxC,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO;YAC7B,aAAa,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI;YACjC,MAAM,EAAE,KAAK,CAAC,MAAM;SACrB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,MAAM,CAAC,IAAI,CACf,kDACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACF,OAAO;YACL,MAAM,EAAE,EAAE;YACV,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,IAAI;YACpB,YAAY,EAAE,QAAQ;SACvB,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,sBAAsB,CAAC;QACvC,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,SAAS,EAAE,KAAK,CAAC,OAAO;QACxB,OAAO,EAAE,KAAK;QACd,GAAG,EAAE,KAAK,CAAC,GAAG;KACf,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE,SAAS,CAAC,MAAM;QACxB,OAAO,EAAE,SAAS,CAAC,SAAS;QAC5B,QAAQ,EAAE,UAAU,CAAC,IAAI;QACzB,YAAY,EAAE,QAAQ;KACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `discoverChannelUrl` — resolves the smee.io channel URL for the doorbell to
3
+ * consume. Four-stage lookup: env override → workspace walk-up →
4
+ * absolute workspace-mirror path → cluster-internal fallback. Never throws;
5
+ * malformed input logs a warning and returns null.
6
+ *
7
+ * Contract: `specs/980-summary-978-shipped-working/contracts/channel-discovery.md`.
8
+ */
9
+ import type { PathLike } from 'node:fs';
10
+ export declare const DEFAULT_CHANNEL_FILE_PATH = "/var/lib/generacy/smee-channel";
11
+ export declare const DEFAULT_WORKSPACE_MIRROR_PATH = "/workspaces/.generacy/cockpit/smee-channel";
12
+ export declare const SMEE_URL_PATTERN: RegExp;
13
+ export type ChannelSource = 'env' | 'workspace-walkup' | 'workspace-absolute' | 'file';
14
+ export interface ChannelDiscoveryResult {
15
+ url: string;
16
+ source: ChannelSource;
17
+ }
18
+ interface ReadFileFn {
19
+ (path: PathLike, encoding: BufferEncoding): Promise<string>;
20
+ }
21
+ export interface ChannelDiscoveryInput {
22
+ env: NodeJS.ProcessEnv;
23
+ channelFilePath: string;
24
+ fs: {
25
+ readFile: ReadFileFn;
26
+ };
27
+ logger?: {
28
+ warn?: (msg: string) => void;
29
+ };
30
+ /** Starting directory for walk-up scan. Defaults to `process.cwd()`. */
31
+ cwd?: string;
32
+ /** Absolute fallback if walk-up produces no hit. */
33
+ workspaceMirrorPath?: string;
34
+ }
35
+ export declare function discoverChannelUrl(input: ChannelDiscoveryInput): Promise<ChannelDiscoveryResult | null>;
36
+ export {};
37
+ //# sourceMappingURL=channel-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-discovery.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/channel-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGxC,eAAO,MAAM,yBAAyB,mCAAmC,CAAC;AAC1E,eAAO,MAAM,6BAA6B,+CACI,CAAC;AAI/C,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AAEvE,MAAM,MAAM,aAAa,GACrB,KAAK,GACL,kBAAkB,GAClB,oBAAoB,GACpB,MAAM,CAAC;AAEX,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,UAAU,UAAU;IAClB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,EAAE,EAAE;QAAE,QAAQ,EAAE,UAAU,CAAA;KAAE,CAAC;IAC7B,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IAC1C,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAyCD,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAmFxC"}
@@ -0,0 +1,92 @@
1
+ import { dirname, join, parse as parsePath } from 'node:path';
2
+ export const DEFAULT_CHANNEL_FILE_PATH = '/var/lib/generacy/smee-channel';
3
+ export const DEFAULT_WORKSPACE_MIRROR_PATH = '/workspaces/.generacy/cockpit/smee-channel';
4
+ // Copied verbatim from packages/orchestrator/src/services/smee-channel-resolver.ts:27
5
+ // to avoid an orchestrator import in the CLI.
6
+ export const SMEE_URL_PATTERN = /^https:\/\/smee\.io\/[A-Za-z0-9_-]+$/;
7
+ const ENV_KEY = 'COCKPIT_DOORBELL_SMEE_URL';
8
+ const WALKUP_SUFFIX = join('.generacy', 'cockpit', 'smee-channel');
9
+ function warn(input, msg) {
10
+ input.logger?.warn?.(msg);
11
+ }
12
+ function isEnoent(err) {
13
+ if (err == null || typeof err !== 'object')
14
+ return false;
15
+ const code = err.code;
16
+ return code === 'ENOENT';
17
+ }
18
+ function errMessage(err) {
19
+ return err instanceof Error ? err.message : String(err);
20
+ }
21
+ async function tryReadUrl(input, path, onNonEnoentError, onMalformed) {
22
+ let raw;
23
+ try {
24
+ raw = await input.fs.readFile(path, 'utf-8');
25
+ }
26
+ catch (err) {
27
+ if (isEnoent(err))
28
+ return null;
29
+ onNonEnoentError(errMessage(err));
30
+ return null;
31
+ }
32
+ const trimmed = raw.trim();
33
+ if (SMEE_URL_PATTERN.test(trimmed)) {
34
+ return trimmed;
35
+ }
36
+ onMalformed(trimmed);
37
+ return null;
38
+ }
39
+ export async function discoverChannelUrl(input) {
40
+ // Stage 1: env override.
41
+ const envRaw = input.env[ENV_KEY];
42
+ const envPresent = envRaw != null && envRaw !== '';
43
+ if (envPresent) {
44
+ if (SMEE_URL_PATTERN.test(envRaw)) {
45
+ return { url: envRaw, source: 'env' };
46
+ }
47
+ warn(input, `cockpit doorbell: ${ENV_KEY} does not match smee URL pattern; falling through to channel file`);
48
+ }
49
+ // Stage 2: walk-up scan starting at cwd.
50
+ const startCwd = input.cwd ?? process.cwd();
51
+ const root = parsePath(startCwd).root;
52
+ let dir = startCwd;
53
+ // Bound the walk: worst case ~few dozen hops, so no infinite-loop guard beyond
54
+ // the root check is required — path.dirname eventually stabilizes at root.
55
+ for (;;) {
56
+ const candidate = join(dir, WALKUP_SUFFIX);
57
+ const url = await tryReadUrl(input, candidate, (msg) => warn(input, `cockpit doorbell: walk-up read failed at ${dir}: ${msg}`), () => warn(input, `cockpit doorbell: channel content at ${candidate} does not match smee URL pattern`));
58
+ if (url != null) {
59
+ return { url, source: 'workspace-walkup' };
60
+ }
61
+ if (dir === root)
62
+ break;
63
+ const parent = dirname(dir);
64
+ if (parent === dir)
65
+ break;
66
+ dir = parent;
67
+ }
68
+ // Stage 3: absolute workspace mirror path.
69
+ const mirrorPath = input.workspaceMirrorPath ?? DEFAULT_WORKSPACE_MIRROR_PATH;
70
+ const mirrorUrl = await tryReadUrl(input, mirrorPath, (msg) => warn(input, `cockpit doorbell: failed to read workspace mirror at ${mirrorPath}: ${msg}`), () => warn(input, `cockpit doorbell: channel content at ${mirrorPath} does not match smee URL pattern`));
71
+ if (mirrorUrl != null) {
72
+ return { url: mirrorUrl, source: 'workspace-absolute' };
73
+ }
74
+ // Stage 4: cluster-internal channel file.
75
+ let raw;
76
+ try {
77
+ raw = await input.fs.readFile(input.channelFilePath, 'utf-8');
78
+ }
79
+ catch (err) {
80
+ if (isEnoent(err))
81
+ return null;
82
+ warn(input, `cockpit doorbell: failed to read channel file ${input.channelFilePath}: ${errMessage(err)}`);
83
+ return null;
84
+ }
85
+ const trimmed = raw.trim();
86
+ if (SMEE_URL_PATTERN.test(trimmed)) {
87
+ return { url: trimmed, source: 'file' };
88
+ }
89
+ warn(input, `cockpit doorbell: channel file ${input.channelFilePath} content does not match smee URL pattern`);
90
+ return null;
91
+ }
92
+ //# sourceMappingURL=channel-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-discovery.js","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/channel-discovery.ts"],"names":[],"mappings":"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"}