@agent-plan/server 0.2.18 → 0.2.19-next.1

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 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAiHpC,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,GAAG,KAAK,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACzF,MAAM,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;CACrC;AAgoBD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,OAAO,GAAG,KAAK,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,KAAK,CAAC;IACX,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA6EvE"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAiHpC,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,GAAG,KAAK,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACzF,MAAM,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;CACrC;AAsuBD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,OAAO,GAAG,KAAK,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,KAAK,CAAC;IACX,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAkFvE"}
package/dist/serve.js CHANGED
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { createAdaptorServer } from "@hono/node-server";
7
7
  import { Hono } from "hono";
8
8
  import { cors } from "hono/cors";
9
- import { ExportService, PlanStore, PlanStoreError, createFeatureId, createPhaseId, createTaskId, normalizeSlug, withFeatureLock, needsMotivation } from "@agent-plan/core";
9
+ import { ExportService, PlanStore, PlanStoreError, createFeatureId, createPhaseId, createShortId, createTaskId, normalizeSlug, withFeatureLock, needsMotivation } from "@agent-plan/core";
10
10
  import { WsHub } from "./ws-hub.js";
11
11
  // ─── Watcher ────────────────────────────────────────────────────────────
12
12
  let watcherAbort = null;
@@ -202,10 +202,14 @@ function createApiApp(store, hubRef, apiPrefix = "", uiConfig, isBusy) {
202
202
  return c.json({ error: "name required" }, 400);
203
203
  const features = await store.loadFeatures();
204
204
  const number = features.features.length + 1;
205
+ const shortId = createShortId(await store.assignedShortIds());
206
+ const priority = await store.nextPriority("feature");
205
207
  const now = nowISO();
206
208
  const feature = {
207
209
  id: createFeatureId(),
208
210
  number,
211
+ shortId,
212
+ priority,
209
213
  name,
210
214
  description: body.description ?? "",
211
215
  status: "planned",
@@ -307,11 +311,15 @@ function createApiApp(store, hubRef, apiPrefix = "", uiConfig, isBusy) {
307
311
  const allPhases = await store.loadAllPhases();
308
312
  const number = allPhases.filter((p) => p.featureId === featureId).length + 1;
309
313
  const slug = normalizeSlug(title);
314
+ const shortId = createShortId(await store.assignedShortIds());
315
+ const priority = await store.nextPriority("phase", featureId);
310
316
  const now = nowISO();
311
317
  phase = {
312
318
  id: createPhaseId(),
313
319
  featureId,
314
320
  number,
321
+ shortId,
322
+ priority,
315
323
  slug,
316
324
  title,
317
325
  status: "draft",
@@ -334,6 +342,8 @@ function createApiApp(store, hubRef, apiPrefix = "", uiConfig, isBusy) {
334
342
  dependsOn: [],
335
343
  createdAt: now,
336
344
  updatedAt: now,
345
+ handoff: "",
346
+ handoffUpdatedAt: "",
337
347
  };
338
348
  await store.savePhase(phase);
339
349
  // Link to feature if featureId provided
@@ -432,10 +442,14 @@ function createApiApp(store, hubRef, apiPrefix = "", uiConfig, isBusy) {
432
442
  const taskNumber = nextTaskNumber(phase);
433
443
  const shortName = normalizeSlug(title).trim() || `task-${Date.now().toString(36)}`;
434
444
  const initialStatus = body.status ?? "planned";
445
+ const shortId = createShortId(await store.assignedShortIds());
446
+ const priority = await store.nextPriority("task", phase.id);
435
447
  const task = {
436
448
  id: createTaskId(),
437
449
  phaseId: phase.id,
438
450
  number: taskNumber,
451
+ shortId,
452
+ priority,
439
453
  shortName,
440
454
  title,
441
455
  status: initialStatus,
@@ -626,25 +640,122 @@ function createApiApp(store, hubRef, apiPrefix = "", uiConfig, isBusy) {
626
640
  hub()?.broadcast({ type: "phases-updated", data: { action: "repaired" } });
627
641
  return c.json(report);
628
642
  });
643
+ // ── Reorder (priority) ───────────────────────────────────────────
644
+ app.post(route("/reorder"), async (c) => {
645
+ const body = await c.req.json();
646
+ const { kind, ids } = body;
647
+ if (!Array.isArray(ids) || ids.length === 0)
648
+ return c.json({ error: "ids required" }, 400);
649
+ const gap = 10;
650
+ if (kind === "feature") {
651
+ // Priority-only: suspend status rollup so reordering never flips a
652
+ // partially-done feature to in-progress (priority ≠ status).
653
+ await store.runBatch(async () => {
654
+ await store.updateFeatures((doc) => {
655
+ for (const [i, id] of ids.entries()) {
656
+ const f = doc.features.find((x) => x.id === id);
657
+ if (f)
658
+ f.priority = (i + 1) * gap;
659
+ }
660
+ doc.features.sort((a, b) => a.priority - b.priority || a.number - b.number);
661
+ return doc;
662
+ });
663
+ });
664
+ hub()?.broadcast({ type: "features-updated", data: { action: "reordered" } });
665
+ }
666
+ else if (kind === "phase") {
667
+ const phases = await store.loadAllPhases();
668
+ await store.runBatch(async () => {
669
+ for (const [i, id] of ids.entries()) {
670
+ if (phases.some((x) => x.id === id)) {
671
+ await store.updatePhase(id, (entry) => { entry.priority = (i + 1) * gap; return entry; });
672
+ }
673
+ }
674
+ });
675
+ hub()?.broadcast({ type: "phases-updated", data: { action: "reordered" } });
676
+ }
677
+ else if (kind === "task") {
678
+ const allPhases = await store.loadAllPhases();
679
+ const host = allPhases.find((p) => p.tasks.some((t) => ids.includes(t.id)));
680
+ if (host) {
681
+ await store.runBatch(async () => {
682
+ await store.updatePhase(host.id, (phase) => {
683
+ for (const [i, id] of ids.entries()) {
684
+ const t = phase.tasks.find((x) => x.id === id);
685
+ if (t)
686
+ t.priority = (i + 1) * gap;
687
+ }
688
+ phase.tasks.sort((a, b) => a.priority - b.priority || a.number - b.number);
689
+ phase.taskIds = phase.tasks.map((t) => t.id);
690
+ return phase;
691
+ });
692
+ });
693
+ hub()?.broadcast({ type: "phases-updated", data: { action: "reordered", phaseId: host.id, featureId: host.featureId } });
694
+ }
695
+ }
696
+ else {
697
+ return c.json({ error: "invalid kind" }, 400);
698
+ }
699
+ await store.writeGenerated();
700
+ hub()?.broadcast({ type: "plan-rendered", data: {} });
701
+ return c.json({ ok: true, kind, count: ids.length });
702
+ });
629
703
  // ── Render ───────────────────────────────────────────────────────
630
704
  app.post(route("/render"), async (c) => {
631
705
  const files = await store.writeGenerated();
632
706
  hub()?.broadcast({ type: "plan-rendered", data: { files } });
633
707
  return c.json({ files });
634
708
  });
635
- // ── Handoff ──────────────────────────────────────────────────────
636
- app.get(route("/handoff"), async (c) => {
637
- const handoff = await store.loadHandoff();
638
- return c.json({
639
- exists: Boolean(handoff),
640
- content: handoff?.content ?? "",
641
- createdAt: handoff?.createdAt ?? "",
642
- updatedAt: handoff?.updatedAt ?? "",
643
- });
709
+ // ── Handoff (entity-scoped, phase.handoff) ────────────────────
710
+ app.get(route("/handoffs"), async (c) => {
711
+ const list = await store.listHandoffs();
712
+ return c.json({ handoffs: list });
644
713
  });
645
- app.delete(route("/handoff"), async (c) => {
646
- await store.deleteHandoff();
647
- return c.json({ deleted: true });
714
+ app.get(route("/phases/:id/handoff"), async (c) => {
715
+ const id = c.req.param("id");
716
+ if (!id)
717
+ return c.json({ error: "id required" }, 400);
718
+ const phase = await store.loadPhase(id).catch(() => null);
719
+ if (!phase)
720
+ return c.json({ error: "phase not found" }, 404);
721
+ const content = await store.getPhaseHandoff(id);
722
+ return c.json({ content, updatedAt: phase.handoffUpdatedAt ?? "" });
723
+ });
724
+ app.put(route("/phases/:id/handoff"), async (c) => {
725
+ const id = c.req.param("id");
726
+ if (!id)
727
+ return c.json({ error: "id required" }, 400);
728
+ const phase = await store.loadPhase(id).catch(() => null);
729
+ if (!phase)
730
+ return c.json({ error: "phase not found" }, 404);
731
+ const body = await c.req.json().catch(() => ({ content: "" }));
732
+ const content = (body?.content ?? "").trim();
733
+ if (!content) {
734
+ // empty PUT = clear equivalent
735
+ await store.clearPhaseHandoff(id);
736
+ hub()?.broadcast({ type: "handoffCleared", data: { phaseId: id } });
737
+ hub()?.broadcast({ type: "phases-updated", data: { action: "updated", id, phaseId: id, featureId: phase.featureId ?? "" } });
738
+ hub()?.broadcast({ type: "plan-rendered", data: {} });
739
+ return c.json({ cleared: true });
740
+ }
741
+ await store.setPhaseHandoff(id, content);
742
+ hub()?.broadcast({ type: "handoffUpdated", data: { phaseId: id } });
743
+ hub()?.broadcast({ type: "phases-updated", data: { action: "updated", id, phaseId: id, featureId: phase.featureId ?? "" } });
744
+ hub()?.broadcast({ type: "plan-rendered", data: {} });
745
+ return c.json({ content, updatedAt: (await store.loadPhase(id)).handoffUpdatedAt ?? "" });
746
+ });
747
+ app.delete(route("/phases/:id/handoff"), async (c) => {
748
+ const id = c.req.param("id");
749
+ if (!id)
750
+ return c.json({ error: "id required" }, 400);
751
+ const phase = await store.loadPhase(id).catch(() => null);
752
+ if (!phase)
753
+ return c.json({ error: "phase not found" }, 404);
754
+ await store.clearPhaseHandoff(id);
755
+ hub()?.broadcast({ type: "handoffCleared", data: { phaseId: id } });
756
+ hub()?.broadcast({ type: "phases-updated", data: { action: "updated", id, phaseId: id, featureId: phase.featureId ?? "" } });
757
+ hub()?.broadcast({ type: "plan-rendered", data: {} });
758
+ return c.json({ cleared: true });
648
759
  });
649
760
  // ── Health ───────────────────────────────────────────────────────
650
761
  app.get(route("/health"), (c) => c.json({ status: "ok", root: store.root }));
@@ -711,6 +822,11 @@ export async function serve(options) {
711
822
  }
712
823
  // Self-heal stale/legacy persisted statuses before the UI reads them.
713
824
  await store.syncStatuses();
825
+ // Backfill missing shortIds (globally-unique 5-char) + priorities so the
826
+ // Web UI always shows short IDs even before the adapter's first-turn init
827
+ // runs. Idempotent: only assigns MISSING shortIds/priorities, never
828
+ // overwrites existing ones.
829
+ await store.ensureShortIdsAndPriority().catch(() => null);
714
830
  // Shared mutable reference — routes see the hub after it's created
715
831
  const hubRef = { current: null };
716
832
  // Default to the bundled web UI when the caller doesn't pass staticDir. Pass
package/dist/ws-hub.d.ts CHANGED
@@ -25,6 +25,16 @@ export type WsEvent = {
25
25
  } | {
26
26
  type: "plan-rendered";
27
27
  data: unknown;
28
+ } | {
29
+ type: "handoffUpdated";
30
+ data: {
31
+ phaseId: string;
32
+ };
33
+ } | {
34
+ type: "handoffCleared";
35
+ data: {
36
+ phaseId: string;
37
+ };
28
38
  };
29
39
  export declare class WsHub {
30
40
  private wss;
@@ -1 +1 @@
1
- {"version":3,"file":"ws-hub.d.ts","sourceRoot":"","sources":["../src/ws-hub.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAE7C,qBAAa,KAAK;IAChB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,KAAK,CAAU;gBAEX,MAAM,EAAE,MAAM,EAAE,KAAK,UAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI;IA0CzE,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAa/B,KAAK,IAAI,IAAI;CASd"}
1
+ {"version":3,"file":"ws-hub.d.ts","sourceRoot":"","sources":["../src/ws-hub.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAE1D,qBAAa,KAAK;IAChB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,KAAK,CAAU;gBAEX,MAAM,EAAE,MAAM,EAAE,KAAK,UAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI;IA0CzE,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAa/B,KAAK,IAAI,IAAI;CASd"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-plan/server",
3
- "version": "0.2.18",
3
+ "version": "0.2.19-next.1",
4
4
  "private": false,
5
5
  "description": "Local HTTP/WebSocket server for Agent Plan dashboards and planner APIs.",
6
6
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  "@hono/node-server": "^1.14.0",
42
42
  "hono": "^4.7.0",
43
43
  "ws": "^8.18.0",
44
- "@agent-plan/core": "0.2.18"
44
+ "@agent-plan/core": "0.2.19-next.1"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/ws": "^8.18.0"