agent-dealer 1.2.13 → 1.2.15

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 (49) hide show
  1. package/bundle/server/dist/adapters/git-worktree.js +15 -0
  2. package/bundle/server/dist/adapters/git-worktree.test.js +30 -0
  3. package/bundle/server/dist/adapters/linear-inbox.js +97 -0
  4. package/bundle/server/dist/adapters/linear-inbox.test.js +299 -1
  5. package/bundle/server/dist/adapters/linear-sync.js +33 -10
  6. package/bundle/server/dist/capacity/claude-events.js +25 -5
  7. package/bundle/server/dist/capacity/claude-local-cache.js +159 -13
  8. package/bundle/server/dist/capacity/claude-local-cache.test.js +191 -3
  9. package/bundle/server/dist/coordinator/auto-merge.js +10 -0
  10. package/bundle/server/dist/coordinator/branch-progress.js +5 -4
  11. package/bundle/server/dist/coordinator/commands.js +37 -3
  12. package/bundle/server/dist/coordinator/delete-issue.js +473 -0
  13. package/bundle/server/dist/coordinator/developer-effect.js +33 -6
  14. package/bundle/server/dist/coordinator/linear-merge-verify.js +382 -0
  15. package/bundle/server/dist/coordinator/linear-merge-verify.test.js +376 -0
  16. package/bundle/server/dist/coordinator/prompts.js +9 -1
  17. package/bundle/server/dist/coordinator/prompts.test.js +93 -0
  18. package/bundle/server/dist/coordinator/source-attachments.js +496 -0
  19. package/bundle/server/dist/coordinator/source-attachments.test.js +396 -0
  20. package/bundle/server/dist/coordinator/source-reload.js +178 -0
  21. package/bundle/server/dist/db/index.js +4 -0
  22. package/bundle/server/dist/db/schema.sql +32 -0
  23. package/bundle/server/dist/docs-linear-attachments.test.js +36 -0
  24. package/bundle/server/dist/paths.js +12 -0
  25. package/bundle/server/dist/repository/runtime-capacity.js +19 -3
  26. package/bundle/server/dist/repository/source-attachments.js +56 -0
  27. package/bundle/server/dist/routes/human-actions.js +17 -0
  28. package/bundle/server/dist/routes/issues-delete.test.js +666 -0
  29. package/bundle/server/dist/routes/issues-execution-contract.test.js +296 -0
  30. package/bundle/server/dist/routes/issues-source-attachments.test.js +447 -0
  31. package/bundle/server/dist/routes/issues.js +109 -1
  32. package/bundle/server/dist/routes/runtime-capacity.test.js +35 -0
  33. package/bundle/server/package.json +2 -2
  34. package/bundle/server/static-ui/assets/index-DVyqblie.js +64 -0
  35. package/bundle/server/static-ui/index.html +1 -1
  36. package/bundle/shared/dist/index.d.ts +108 -2
  37. package/bundle/shared/dist/index.js +12 -1
  38. package/bundle/shared/dist/issues.d.ts +43 -2
  39. package/bundle/shared/dist/issues.js +10 -0
  40. package/bundle/shared/dist/linear-intake.d.ts +85 -0
  41. package/bundle/shared/dist/linear-intake.js +56 -0
  42. package/bundle/shared/dist/linear-intake.test.js +55 -1
  43. package/bundle/shared/dist/runtime-capacity.d.ts +147 -0
  44. package/bundle/shared/dist/runtime-capacity.js +15 -0
  45. package/bundle/shared/dist/workflow.d.ts +4 -4
  46. package/bundle/shared/dist/workflow.js +7 -0
  47. package/bundle/shared/package.json +1 -1
  48. package/package.json +1 -1
  49. package/bundle/server/static-ui/assets/index-BzLjMBI9.js +0 -64
@@ -35,6 +35,18 @@ export function cleanupOrphanedWorkerMcpConfig() {
35
35
  fs.rmSync(path.join(dir, entry), { recursive: true, force: true });
36
36
  }
37
37
  }
38
+ /**
39
+ * NOT-364: Dealer-owned blobs for Linear issue-source attachments. Content is
40
+ * addressed under `<dir>/<issueId>/`; rows in `issue_source_attachments` point
41
+ * here. Retained with the issue history — worktree copies are removed with
42
+ * normal worktree cleanup, but these blobs survive until the issue row itself
43
+ * is gone (and there is no hard-delete path: `closed` issues stay queryable).
44
+ */
45
+ export function getSourceAttachmentsDir() {
46
+ const dir = path.join(getDataDir(), "source-attachments");
47
+ fs.mkdirSync(dir, { recursive: true });
48
+ return dir;
49
+ }
38
50
  /**
39
51
  * Root for Dealer-managed repository clones and session worktrees (NOT-149).
40
52
  * Defaults under AGENT_DEALER_HOME; override with AGENT_DEALER_EXECUTION_ROOT for a
@@ -9,7 +9,20 @@
9
9
  //
10
10
  // Diagnostic evidence stays server-side: `evidence_ref` is a pointer
11
11
  // (log path, probe id), never credentials and never a raw provider payload.
12
+ import { CapacityUnavailableDetail } from "@agent-dealer/shared";
12
13
  import { getDb } from "../db/index.js";
14
+ /** Stored JSON → validated detail; anything malformed reads as absent. */
15
+ function parseUnavailableDetail(raw) {
16
+ if (!raw)
17
+ return null;
18
+ try {
19
+ const parsed = CapacityUnavailableDetail.safeParse(JSON.parse(raw));
20
+ return parsed.success ? parsed.data : null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
13
26
  function rowToSnapshot(row) {
14
27
  return {
15
28
  windowKey: row.window_key,
@@ -25,6 +38,7 @@ function rowToSnapshot(row) {
25
38
  expiresAt: row.expires_at,
26
39
  source: row.source,
27
40
  unavailableReason: row.unavailable_reason,
41
+ unavailableDetail: parseUnavailableDetail(row.unavailable_detail),
28
42
  criticalRole: row.critical_role,
29
43
  };
30
44
  }
@@ -39,13 +53,13 @@ export function recordCapacitySnapshots(runtime, windows) {
39
53
  runtime, window_key, provider_bucket, duration_minutes, display_label,
40
54
  used_value, used_unit, remaining_percent, reset_at, observed_at,
41
55
  fresh_until, expires_at, source, unavailable_reason, evidence_ref,
42
- critical_role
56
+ critical_role, unavailable_detail
43
57
  )
44
58
  VALUES (
45
59
  @runtime, @window_key, @provider_bucket, @duration_minutes, @display_label,
46
60
  @used_value, @used_unit, @remaining_percent, @reset_at, @observed_at,
47
61
  @fresh_until, @expires_at, @source, @unavailable_reason, @evidence_ref,
48
- @critical_role
62
+ @critical_role, @unavailable_detail
49
63
  )
50
64
  ON CONFLICT(runtime, window_key) DO UPDATE SET
51
65
  provider_bucket = excluded.provider_bucket,
@@ -61,7 +75,8 @@ export function recordCapacitySnapshots(runtime, windows) {
61
75
  source = excluded.source,
62
76
  unavailable_reason = excluded.unavailable_reason,
63
77
  evidence_ref = excluded.evidence_ref,
64
- critical_role = excluded.critical_role
78
+ critical_role = excluded.critical_role,
79
+ unavailable_detail = excluded.unavailable_detail
65
80
  `);
66
81
  const run = getDb().transaction((rows) => {
67
82
  for (const w of rows) {
@@ -82,6 +97,7 @@ export function recordCapacitySnapshots(runtime, windows) {
82
97
  unavailable_reason: w.unavailableReason ?? null,
83
98
  evidence_ref: w.evidenceRef ?? null,
84
99
  critical_role: w.criticalRole ?? null,
100
+ unavailable_detail: w.unavailableDetail ? JSON.stringify(w.unavailableDetail) : null,
85
101
  });
86
102
  }
87
103
  });
@@ -0,0 +1,56 @@
1
+ // packages/server/src/repository/source-attachments.ts
2
+ //
3
+ // NOT-364: read/write side for `issue_source_attachments` — durable snapshots
4
+ // of a Linear issue's own file/link attachments, keyed by
5
+ // (Dealer issue id, Linear attachment id). Hosted files carry a Dealer-owned
6
+ // blob_path plus integrity fields; links carry source metadata only.
7
+ import { v4 as uuid } from "uuid";
8
+ import { getDb } from "../db/index.js";
9
+ function rowToRecord(row) {
10
+ const out = {
11
+ linearAttachmentId: row.linear_attachment_id,
12
+ kind: row.kind === "file" ? "file" : "link",
13
+ title: row.title,
14
+ url: row.url,
15
+ };
16
+ if (row.safe_file_name)
17
+ out.safeFileName = row.safe_file_name;
18
+ if (row.blob_path)
19
+ out.blobPath = row.blob_path;
20
+ if (row.content_type)
21
+ out.contentType = row.content_type;
22
+ if (row.size_bytes !== null)
23
+ out.sizeBytes = row.size_bytes;
24
+ if (row.sha256)
25
+ out.sha256 = row.sha256;
26
+ if (row.subtitle)
27
+ out.subtitle = row.subtitle;
28
+ if (row.source)
29
+ out.source = row.source;
30
+ return out;
31
+ }
32
+ /** Every source attachment for the issue, oldest first (stable prompt order). */
33
+ export function listSourceAttachments(issueId) {
34
+ const rows = getDb()
35
+ .prepare("SELECT * FROM issue_source_attachments WHERE issue_id = ? ORDER BY created_at ASC, rowid ASC")
36
+ .all(issueId);
37
+ return rows.map(rowToRecord);
38
+ }
39
+ /**
40
+ * All-or-nothing replacement of an issue's attachment snapshot: deletes every
41
+ * existing row for the issue, then inserts the new set. Call inside the
42
+ * caller's transaction when the replacement must commit atomically with other
43
+ * writes (import text, reload text) — a throw leaves the prior rows untouched.
44
+ */
45
+ export function replaceSourceAttachments(issueId, records) {
46
+ const db = getDb();
47
+ const now = new Date().toISOString();
48
+ db.prepare("DELETE FROM issue_source_attachments WHERE issue_id = ?").run(issueId);
49
+ const insert = db.prepare(`INSERT INTO issue_source_attachments
50
+ (id, issue_id, linear_attachment_id, kind, title, safe_file_name, blob_path,
51
+ content_type, size_bytes, sha256, url, subtitle, source, created_at, updated_at)
52
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
53
+ for (const r of records) {
54
+ insert.run(uuid(), issueId, r.linearAttachmentId, r.kind, r.title, r.safeFileName ?? null, r.blobPath ?? null, r.contentType ?? null, r.sizeBytes ?? null, r.sha256 ?? null, r.url, r.subtitle ?? null, r.source ?? null, now, now);
55
+ }
56
+ }
@@ -2,6 +2,7 @@ import { getHumanAction, listOpenHumanActions } from "../repository/human-action
2
2
  import { resolveHumanActionAndAdvanceAsync } from "../coordinator/commands.js";
3
3
  import { normalizeResolutionNote } from "../coordinator/human-resolution.js";
4
4
  import { triggerIssueReflect, resolveReflectionInteractionAction } from "../coordinator/reflect-trigger.js";
5
+ import { isLinearMergeStaleAction, resolveLinearMergeStaleAction, } from "../coordinator/linear-merge-verify.js";
5
6
  import { resolveOutboundDeliveryAction } from "../queue/approve-deliver.js";
6
7
  export async function registerHumanActionRoutes(app) {
7
8
  app.get("/api/human-actions", async () => listOpenHumanActions());
@@ -50,6 +51,22 @@ export async function registerHumanActionRoutes(app) {
50
51
  return reply.status(deliveryResult.code).send({ error: deliveryResult.error });
51
52
  return { runStatus: deliveryResult.runStatus, delivered: deliveryResult.delivered };
52
53
  }
54
+ // NOT-362: the stale-source notice sits on an already-`done` issue with no
55
+ // active workflow to advance — the generic resolver would 409 on "no active
56
+ // workflow for this action" and the notice could never close. Resolve it
57
+ // directly (same shape as the reflection branch above); `recheck` re-runs
58
+ // the post-merge verification fire-and-forget.
59
+ if (action.actionType === "policy_escalation" && isLinearMergeStaleAction(action)) {
60
+ const staleResult = resolveLinearMergeStaleAction(id, resolvedBy, choice);
61
+ if (!staleResult.ok)
62
+ return reply.status(staleResult.code).send({ error: staleResult.error });
63
+ return {
64
+ issueStatus: staleResult.issueStatus,
65
+ nextWorkItemId: null,
66
+ instanceCompleted: false,
67
+ restarted: false,
68
+ };
69
+ }
53
70
  // Awaits undraft+merge when final_review:complete (NOT-102) — sync resolve alone would
54
71
  // only park at AUTO_MERGE_INTENT and leave the PR draft.
55
72
  const result = await resolveHumanActionAndAdvanceAsync(id, resolvedBy, choice, {