syntaur 0.46.0 → 0.48.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 (72) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dashboard/dist/assets/{_basePickBy-RQBuJKcX.js → _basePickBy-Cie7FXcV.js} +1 -1
  3. package/dashboard/dist/assets/{_baseUniq-_J7s4kD3.js → _baseUniq-C2af7-lW.js} +1 -1
  4. package/dashboard/dist/assets/{arc-_9SyUgKQ.js → arc-BHKGjEv8.js} +1 -1
  5. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-C8LeFMgr.js → architectureDiagram-2XIMDMQ5-CWqzcG1t.js} +1 -1
  6. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-gMh0EPEh.js → blockDiagram-WCTKOSBZ-CHLC0M63.js} +1 -1
  7. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-cHwecwLI.js → c4Diagram-IC4MRINW-Bx6GTZzJ.js} +1 -1
  8. package/dashboard/dist/assets/channel-CXlttLwe.js +1 -0
  9. package/dashboard/dist/assets/{chunk-4BX2VUAB-Bb2anYuQ.js → chunk-4BX2VUAB-7cTgwG6c.js} +1 -1
  10. package/dashboard/dist/assets/{chunk-55IACEB6-DYIRGzA1.js → chunk-55IACEB6-DQmdA20e.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-FMBD7UC4-sgRWBbaF.js → chunk-FMBD7UC4-knmOnIKI.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-JSJVCQXG-DlYKMl_j.js → chunk-JSJVCQXG-CaiLHJFn.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-KX2RTZJC-D0YDLAOF.js → chunk-KX2RTZJC-Crp4zWuY.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-NQ4KR5QH-D-Y-CUx6.js → chunk-NQ4KR5QH-DDjtdNaK.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-QZHKN3VN-D7FpSvb5.js → chunk-QZHKN3VN-Cbm1IieP.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-WL4C6EOR-CtXgQLdS.js → chunk-WL4C6EOR-CdrRh6R4.js} +1 -1
  17. package/dashboard/dist/assets/classDiagram-VBA2DB6C-BJM0Dprk.js +1 -0
  18. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BJM0Dprk.js +1 -0
  19. package/dashboard/dist/assets/clone-BTq7ueDt.js +1 -0
  20. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-YbTaohoJ.js → cose-bilkent-S5V4N54A-DyNajnMF.js} +1 -1
  21. package/dashboard/dist/assets/{dagre-KLK3FWXG-CMtwGAnP.js → dagre-KLK3FWXG-CIBjpfqE.js} +1 -1
  22. package/dashboard/dist/assets/{diagram-E7M64L7V-D8wBMBAX.js → diagram-E7M64L7V-CDENlpZN.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-IFDJBPK2-DfudLpiJ.js → diagram-IFDJBPK2-CIGMJoFB.js} +1 -1
  24. package/dashboard/dist/assets/{diagram-P4PSJMXO-CyMy61wE.js → diagram-P4PSJMXO-C27fj5wp.js} +1 -1
  25. package/dashboard/dist/assets/{erDiagram-INFDFZHY-BlB4ZQl9.js → erDiagram-INFDFZHY-M2CywJym.js} +1 -1
  26. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-DbhDQJM3.js → flowDiagram-PKNHOUZH-CTV6ROYf.js} +1 -1
  27. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-DJFqteNi.js → ganttDiagram-A5KZAMGK-CxH3f3e5.js} +1 -1
  28. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-D8etA_mm.js → gitGraphDiagram-K3NZZRJ6-CwhnVvHE.js} +1 -1
  29. package/dashboard/dist/assets/{graph-Ce86jeZn.js → graph-DGkI_ekZ.js} +1 -1
  30. package/dashboard/dist/assets/{index-DzHQIE2n.css → index-BxO2I5dN.css} +1 -1
  31. package/dashboard/dist/assets/index-CTdFARW9.js +567 -0
  32. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-Cx35U-h8.js → infoDiagram-LFFYTUFH-CuKReAi2.js} +1 -1
  33. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-C04Y2nj8.js → ishikawaDiagram-PHBUUO56-DyBMt5Mw.js} +1 -1
  34. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-D8-cxbxE.js → journeyDiagram-4ABVD52K-CnwtbTNs.js} +1 -1
  35. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-DVKqMylP.js → kanban-definition-K7BYSVSG-5Sh1bC5j.js} +1 -1
  36. package/dashboard/dist/assets/{layout-98xZDpgu.js → layout-W2Vytcip.js} +1 -1
  37. package/dashboard/dist/assets/{linear-0jk_IwAc.js → linear-UTm9E6c4.js} +1 -1
  38. package/dashboard/dist/assets/{mermaid.core-C337VWfr.js → mermaid.core-B0EB-dBk.js} +4 -4
  39. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-8sNYGYEP.js → mindmap-definition-YRQLILUH-CH4L08eg.js} +1 -1
  40. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-afcmzHxf.js → pieDiagram-SKSYHLDU-CQ3PMl-w.js} +1 -1
  41. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-B4RjcpOq.js → quadrantDiagram-337W2JSQ-DbZgXUm5.js} +1 -1
  42. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-CRavU6cI.js → requirementDiagram-Z7DCOOCP-CxJWytQa.js} +1 -1
  43. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-DFomU3z-.js → sankeyDiagram-WA2Y5GQK-3YeEasvh.js} +1 -1
  44. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-CGKO7nmK.js → sequenceDiagram-2WXFIKYE-F2EyvYsW.js} +1 -1
  45. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-BjFI1K8h.js → stateDiagram-RAJIS63D-DrmiemaD.js} +1 -1
  46. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DfyvP5EF.js +1 -0
  47. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-BBo8XJFG.js → timeline-definition-YZTLITO2-CBee2YiE.js} +1 -1
  48. package/dashboard/dist/assets/{treemap-KZPCXAKY-COd6i6TE.js → treemap-KZPCXAKY-BkPRIS3K.js} +1 -1
  49. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-CGQweQ36.js → vennDiagram-LZ73GAT5-CTsfwv8G.js} +1 -1
  50. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-mfJ5So7N.js → xychartDiagram-JWTSCODW-DUSo-c6w.js} +1 -1
  51. package/dashboard/dist/index.html +2 -2
  52. package/dist/dashboard/server.js +188 -73
  53. package/dist/dashboard/server.js.map +1 -1
  54. package/dist/index.js +198 -74
  55. package/dist/index.js.map +1 -1
  56. package/dist/launch/index.d.ts +25 -12
  57. package/dist/launch/index.js +62 -58
  58. package/dist/launch/index.js.map +1 -1
  59. package/package.json +1 -1
  60. package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
  61. package/platforms/claude-code/skills/manage-statuses/SKILL.md +7 -0
  62. package/platforms/codex/.codex-plugin/plugin.json +1 -1
  63. package/platforms/codex/skills/manage-statuses/SKILL.md +7 -0
  64. package/platforms/hermes/plugins/syntaur/__pycache__/__init__.cpython-312.pyc +0 -0
  65. package/platforms/hermes/plugins/syntaur/__pycache__/boundary.cpython-312.pyc +0 -0
  66. package/skills/manage-statuses/SKILL.md +7 -0
  67. package/dashboard/dist/assets/channel-C36dnl_e.js +0 -1
  68. package/dashboard/dist/assets/classDiagram-VBA2DB6C-BsoGa6_a.js +0 -1
  69. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BsoGa6_a.js +0 -1
  70. package/dashboard/dist/assets/clone-Bz6jW3OY.js +0 -1
  71. package/dashboard/dist/assets/index-DRng26Jg.js +0 -567
  72. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-BtxefYKD.js +0 -1
package/dist/index.js CHANGED
@@ -3107,6 +3107,53 @@ function factFieldNames(decl) {
3107
3107
  ].map((k) => k.toLowerCase()) : [exportNames.fact.toLowerCase()];
3108
3108
  return { storageKey: name, exports: exportNames, registryKeys };
3109
3109
  }
3110
+ function validateFactDeclarations(raw2) {
3111
+ const problems = [];
3112
+ const owners = /* @__PURE__ */ new Map();
3113
+ for (const key of Object.keys(DERIVE_FIELDS)) owners.set(key, "built-in");
3114
+ for (const key of Object.keys(ASSIGNMENT_FIELDS)) owners.set(key, "built-in");
3115
+ for (const row of raw2 ?? []) {
3116
+ const name = (row?.name ?? "").trim();
3117
+ if (!/^[a-z][a-zA-Z0-9]*$/.test(name)) {
3118
+ problems.push(
3119
+ `fact "${row?.name ?? ""}": invalid name \u2014 must match /^[a-z][a-zA-Z0-9]*$/`
3120
+ );
3121
+ continue;
3122
+ }
3123
+ const type = (row.type ?? "").trim();
3124
+ if (type !== "bool" && type !== "number" && type !== "attestation") {
3125
+ problems.push(
3126
+ `fact "${name}": invalid type "${row.type ?? ""}" \u2014 expected bool, number, or attestation`
3127
+ );
3128
+ continue;
3129
+ }
3130
+ if (type === "attestation") {
3131
+ const binds = (row.binds ?? "none").toString().trim() || "none";
3132
+ if (binds !== "plan" && binds !== "commit" && binds !== "none") {
3133
+ problems.push(
3134
+ `fact "${name}": invalid binds "${row.binds}" \u2014 expected plan, commit, or none`
3135
+ );
3136
+ continue;
3137
+ }
3138
+ }
3139
+ const decl = type === "attestation" ? { name, type, binds: "none" } : { name, type };
3140
+ const keys = factFieldNames(decl).registryKeys;
3141
+ const collidingKey = keys.find((k) => owners.has(k));
3142
+ if (collidingKey !== void 0) {
3143
+ const owner = owners.get(collidingKey);
3144
+ if (owner === "built-in") {
3145
+ problems.push(`fact "${name}": exported field "${collidingKey}" collides with a built-in field`);
3146
+ } else if (owner === name) {
3147
+ problems.push(`fact "${name}": duplicate declaration (a fact named "${name}" is already declared)`);
3148
+ } else {
3149
+ problems.push(`fact "${name}": exported field "${collidingKey}" collides with fact "${owner}"`);
3150
+ }
3151
+ continue;
3152
+ }
3153
+ for (const key of keys) owners.set(key, name);
3154
+ }
3155
+ return problems;
3156
+ }
3110
3157
  function acceptFactDeclarations(declarations) {
3111
3158
  const taken = /* @__PURE__ */ new Set([
3112
3159
  ...Object.keys(DERIVE_FIELDS),
@@ -3632,7 +3679,6 @@ function parseStatusConfig(content) {
3632
3679
  continue;
3633
3680
  }
3634
3681
  }
3635
- if (statuses.length === 0) return null;
3636
3682
  const derive = phaseLadder.length > 0 || disposition.length > 0 || Object.keys(headline).length > 0 ? {
3637
3683
  phaseLadder: phaseLadder.length > 0 ? phaseLadder : DEFAULT_DERIVE_CONFIG.phaseLadder,
3638
3684
  disposition: disposition.length > 0 ? disposition : DEFAULT_DERIVE_CONFIG.disposition,
@@ -3643,6 +3689,7 @@ function parseStatusConfig(content) {
3643
3689
  active: "phase"
3644
3690
  }
3645
3691
  } : null;
3692
+ if (statuses.length === 0 && facts.length === 0 && derive === null) return null;
3646
3693
  return {
3647
3694
  statuses,
3648
3695
  order: order.length > 0 ? order : statuses.map((s) => s.id),
@@ -3768,53 +3815,6 @@ function validateDeriveConfig(derive, statusConfig, validateWhen = () => null) {
3768
3815
  }
3769
3816
  return problems;
3770
3817
  }
3771
- function validateFactDeclarations(raw2) {
3772
- const problems = [];
3773
- const owners = /* @__PURE__ */ new Map();
3774
- for (const key of Object.keys(DERIVE_FIELDS)) owners.set(key, "built-in");
3775
- for (const key of Object.keys(ASSIGNMENT_FIELDS)) owners.set(key, "built-in");
3776
- for (const row of raw2 ?? []) {
3777
- const name = (row?.name ?? "").trim();
3778
- if (!/^[a-z][a-zA-Z0-9]*$/.test(name)) {
3779
- problems.push(
3780
- `fact "${row?.name ?? ""}": invalid name \u2014 must match /^[a-z][a-zA-Z0-9]*$/`
3781
- );
3782
- continue;
3783
- }
3784
- const type = (row.type ?? "").trim();
3785
- if (type !== "bool" && type !== "number" && type !== "attestation") {
3786
- problems.push(
3787
- `fact "${name}": invalid type "${row.type ?? ""}" \u2014 expected bool, number, or attestation`
3788
- );
3789
- continue;
3790
- }
3791
- if (type === "attestation") {
3792
- const binds = (row.binds ?? "none").toString().trim() || "none";
3793
- if (binds !== "plan" && binds !== "commit" && binds !== "none") {
3794
- problems.push(
3795
- `fact "${name}": invalid binds "${row.binds}" \u2014 expected plan, commit, or none`
3796
- );
3797
- continue;
3798
- }
3799
- }
3800
- const decl = type === "attestation" ? { name, type, binds: "none" } : { name, type };
3801
- const keys = factFieldNames(decl).registryKeys;
3802
- const collidingKey = keys.find((k) => owners.has(k));
3803
- if (collidingKey !== void 0) {
3804
- const owner = owners.get(collidingKey);
3805
- if (owner === "built-in") {
3806
- problems.push(`fact "${name}": exported field "${collidingKey}" collides with a built-in field`);
3807
- } else if (owner === name) {
3808
- problems.push(`fact "${name}": duplicate declaration (a fact named "${name}" is already declared)`);
3809
- } else {
3810
- problems.push(`fact "${name}": exported field "${collidingKey}" collides with fact "${owner}"`);
3811
- }
3812
- continue;
3813
- }
3814
- for (const key of keys) owners.set(key, name);
3815
- }
3816
- return problems;
3817
- }
3818
3818
  function serializeIntegrationConfig(integrations) {
3819
3819
  const lines = [];
3820
3820
  if (integrations.claudePluginDir) {
@@ -4894,7 +4894,6 @@ var init_config2 = __esm({
4894
4894
  init_agents_schema();
4895
4895
  init_slug();
4896
4896
  init_fact_registry();
4897
- init_query();
4898
4897
  init_terminal_schema();
4899
4898
  init_workspace_visibility_schema();
4900
4899
  DEFAULT_DERIVE_CONFIG = {
@@ -7301,7 +7300,8 @@ function rowToSession(row) {
7301
7300
  transcriptPath: row.transcript_path ?? null,
7302
7301
  pid: row.pid ?? null,
7303
7302
  pidStartedAt: row.pid_started_at ?? null,
7304
- originalHeadSha: row.original_head_sha ?? null
7303
+ originalHeadSha: row.original_head_sha ?? null,
7304
+ updatedAt: row.updated_at ?? null
7305
7305
  };
7306
7306
  }
7307
7307
  async function parseSessionsIndex(_projectDir, projectSlug) {
@@ -8765,24 +8765,28 @@ async function getStatusConfig() {
8765
8765
  if (_cachedConfig) return _cachedConfig;
8766
8766
  const config = await readConfig();
8767
8767
  if (config.statuses) {
8768
+ const sc = config.statuses;
8769
+ const defaults = sc.statuses.length === 0 ? buildDefaultStatusConfig() : null;
8770
+ const effectiveStatuses = defaults ? defaults.statuses : sc.statuses;
8771
+ const effectiveOrder = defaults ? defaults.order : sc.order;
8768
8772
  const terminalSet = new Set(
8769
- config.statuses.statuses.filter((s) => s.terminal).map((s) => s.id)
8773
+ effectiveStatuses.filter((s) => s.terminal).map((s) => s.id)
8770
8774
  );
8771
- const hasCustomTransitions = config.statuses.transitions.length > 0;
8772
- const effectiveTransitions2 = hasCustomTransitions ? config.statuses.transitions : Array.from(DEFAULT_TRANSITION_TABLE.entries()).map(([key, to]) => {
8775
+ const hasCustomTransitions = sc.transitions.length > 0;
8776
+ const effectiveTransitions2 = hasCustomTransitions ? sc.transitions : Array.from(DEFAULT_TRANSITION_TABLE.entries()).map(([key, to]) => {
8773
8777
  const [from, command] = key.split(":");
8774
8778
  return { from, command, to };
8775
8779
  });
8776
- const accepted = acceptFactDeclarations(normalizeFactDeclarations(config.statuses.facts ?? null));
8780
+ const accepted = acceptFactDeclarations(normalizeFactDeclarations(sc.facts ?? null));
8777
8781
  _cachedConfig = {
8778
8782
  custom: true,
8779
- statuses: config.statuses.statuses,
8780
- order: config.statuses.order,
8783
+ statuses: effectiveStatuses,
8784
+ order: effectiveOrder,
8781
8785
  transitions: effectiveTransitions2,
8782
8786
  transitionTable: buildTransitionTable(effectiveTransitions2),
8783
8787
  terminalStatuses: terminalSet.size > 0 ? terminalSet : /* @__PURE__ */ new Set(["completed", "failed"]),
8784
- derive: config.statuses.derive ?? null,
8785
- facts: config.statuses.facts ?? null,
8788
+ derive: sc.derive ?? null,
8789
+ facts: sc.facts ?? null,
8786
8790
  factDeclarations: accepted,
8787
8791
  deriveRegistry: buildDeriveRegistry(accepted),
8788
8792
  queryRegistry: buildQueryRegistry(accepted)
@@ -9946,7 +9950,7 @@ async function buildDerivedDetail(assignment, assignmentDir, projectDir) {
9946
9950
  try {
9947
9951
  const { computeFactsDetailed: computeFactsDetailed2 } = await Promise.resolve().then(() => (init_facts(), facts_exports));
9948
9952
  const { deriveDimensions: deriveDimensions2 } = await Promise.resolve().then(() => (init_derive(), derive_exports));
9949
- const { DEFAULT_DERIVE_CONFIG: DEFAULT_DERIVE_CONFIG2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
9953
+ const { DEFAULT_DERIVE_CONFIG: DEFAULT_DERIVE_CONFIG3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
9950
9954
  const { facts, attestations } = await computeFactsDetailed2({
9951
9955
  assignmentDir,
9952
9956
  frontmatter: {
@@ -9961,7 +9965,7 @@ async function buildDerivedDetail(assignment, assignmentDir, projectDir) {
9961
9965
  });
9962
9966
  const dims = deriveDimensions2({
9963
9967
  facts,
9964
- derive: config.derive ?? DEFAULT_DERIVE_CONFIG2,
9968
+ derive: config.derive ?? DEFAULT_DERIVE_CONFIG3,
9965
9969
  currentStatus: assignment.status,
9966
9970
  terminalStatuses: config.terminalStatuses,
9967
9971
  knownStatusIds: new Set(config.statuses.map((s) => s.id)),
@@ -13871,7 +13875,11 @@ var SORT_FIELDS = [
13871
13875
  "assignee",
13872
13876
  "dependencies",
13873
13877
  "created",
13874
- "updated"
13878
+ "updated",
13879
+ "started",
13880
+ "lastActivity",
13881
+ "projectName",
13882
+ "agentName"
13875
13883
  ];
13876
13884
  var SORT_DIRECTIONS = ["asc", "desc"];
13877
13885
  var DENSITIES = ["comfortable", "compact"];
@@ -13884,7 +13892,7 @@ var GROUPINGS = [
13884
13892
  "project"
13885
13893
  ];
13886
13894
  var ACTIVITIES = ["all", "stale", "fresh"];
13887
- var DATE_RANGE_FIELDS = ["created", "updated"];
13895
+ var DATE_RANGE_FIELDS = ["created", "updated", "started"];
13888
13896
  var DATE_RANGE_PRESETS = [
13889
13897
  "last_24h",
13890
13898
  "last_7d",
@@ -14133,6 +14141,7 @@ function makeSeededView(id, name, filterOverrides) {
14133
14141
  id,
14134
14142
  name,
14135
14143
  workspace: null,
14144
+ entityType: "assignment",
14136
14145
  config: {
14137
14146
  viewMode: "list",
14138
14147
  filters: { ...DEFAULT_FILTERS, ...filterOverrides },
@@ -14170,7 +14179,13 @@ function isWidgetConfig(value) {
14170
14179
  if (obj.kind === "saved-view") {
14171
14180
  return typeof obj.viewId === "string" && obj.viewId.length > 0;
14172
14181
  }
14173
- return obj.kind === "agent-sessions" || obj.kind === "inventories";
14182
+ if (obj.kind === "agent-sessions") {
14183
+ if (obj.viewId !== void 0) {
14184
+ return typeof obj.viewId === "string" && obj.viewId.length > 0;
14185
+ }
14186
+ return true;
14187
+ }
14188
+ return obj.kind === "inventories";
14174
14189
  }
14175
14190
  function isListSectionVisibility(value) {
14176
14191
  if (!value || typeof value !== "object") return false;
@@ -14207,6 +14222,9 @@ function isSavedViewConfig(value) {
14207
14222
  function isSavedView(value) {
14208
14223
  if (!value || typeof value !== "object") return false;
14209
14224
  const obj = value;
14225
+ if (obj.entityType !== void 0 && obj.entityType !== "assignment" && obj.entityType !== "session") {
14226
+ return false;
14227
+ }
14210
14228
  return typeof obj.id === "string" && obj.id.length > 0 && typeof obj.name === "string" && (obj.workspace === null || typeof obj.workspace === "string") && isSavedViewConfig(obj.config) && typeof obj.createdAt === "string" && typeof obj.updatedAt === "string";
14211
14229
  }
14212
14230
  function isDashboardSlot(value) {
@@ -14301,6 +14319,8 @@ function createSavedView(file, input4, now = () => (/* @__PURE__ */ new Date()).
14301
14319
  id: randomUUID2(),
14302
14320
  name: input4.name,
14303
14321
  workspace: input4.workspace,
14322
+ // Only persist entityType when explicitly a session view; absent === assignment.
14323
+ ...input4.entityType === "session" ? { entityType: "session" } : {},
14304
14324
  config: input4.config,
14305
14325
  createdAt: ts,
14306
14326
  updatedAt: ts
@@ -14399,12 +14419,16 @@ function validateCreateBody(body) {
14399
14419
  if (!isSavedViewConfig(obj.config)) {
14400
14420
  return { ok: false, error: "config must be a valid SavedViewConfig" };
14401
14421
  }
14422
+ if (obj.entityType !== void 0 && obj.entityType !== "assignment" && obj.entityType !== "session") {
14423
+ return { ok: false, error: "entityType must be 'assignment' or 'session'" };
14424
+ }
14402
14425
  return {
14403
14426
  ok: true,
14404
14427
  value: {
14405
14428
  name: obj.name.trim(),
14406
14429
  workspace: obj.workspace,
14407
- config: obj.config
14430
+ config: obj.config,
14431
+ ...obj.entityType !== void 0 ? { entityType: obj.entityType } : {}
14408
14432
  }
14409
14433
  };
14410
14434
  }
@@ -19359,7 +19383,9 @@ function createWorkspaceVisibilityConfigRouter() {
19359
19383
 
19360
19384
  // src/dashboard/api-status-config.ts
19361
19385
  init_config2();
19386
+ init_fact_registry();
19362
19387
  init_api();
19388
+ init_derive();
19363
19389
  import { Router as Router9 } from "express";
19364
19390
 
19365
19391
  // src/utils/status-config-resolution.ts
@@ -19767,7 +19793,8 @@ function createStatusConfigRouter(projectsDir2, assignmentsDir2) {
19767
19793
  order: config.order,
19768
19794
  transitions: config.transitions,
19769
19795
  custom: config.custom,
19770
- factDeclarations: config.factDeclarations
19796
+ factDeclarations: config.factDeclarations,
19797
+ rawFacts: config.facts ?? []
19771
19798
  });
19772
19799
  } catch (error) {
19773
19800
  console.error("Error getting status config:", error);
@@ -19791,8 +19818,27 @@ function createStatusConfigRouter(projectsDir2, assignmentsDir2) {
19791
19818
  });
19792
19819
  router.post("/", async (req, res) => {
19793
19820
  try {
19794
- const { statuses, order, transitions, resolutions: rawResolutions } = req.body ?? {};
19795
- if (!Array.isArray(statuses) || !Array.isArray(order) || !Array.isArray(transitions)) {
19821
+ const {
19822
+ statuses,
19823
+ order,
19824
+ transitions,
19825
+ facts: bodyFacts,
19826
+ factRemovalAcks,
19827
+ resolutions: rawResolutions
19828
+ } = req.body ?? {};
19829
+ const currentConfig = await getStatusConfig();
19830
+ const hasFacts = bodyFacts !== void 0;
19831
+ const hasStatuses = statuses !== void 0;
19832
+ const hasOrder = order !== void 0;
19833
+ const hasTransitions = transitions !== void 0;
19834
+ let effectiveStatuses = statuses;
19835
+ let effectiveOrder = order;
19836
+ let effectiveTransitions2 = transitions;
19837
+ if (hasFacts && !hasStatuses && !hasOrder && !hasTransitions) {
19838
+ effectiveStatuses = currentConfig.statuses;
19839
+ effectiveOrder = currentConfig.order;
19840
+ effectiveTransitions2 = currentConfig.transitions;
19841
+ } else if (!Array.isArray(effectiveStatuses) || !Array.isArray(effectiveOrder) || !Array.isArray(effectiveTransitions2)) {
19796
19842
  res.status(400).json({ error: "malformed-statuses", message: "Request body must include statuses, order, and transitions arrays" });
19797
19843
  return;
19798
19844
  }
@@ -19806,10 +19852,9 @@ function createStatusConfigRouter(projectsDir2, assignmentsDir2) {
19806
19852
  return;
19807
19853
  }
19808
19854
  const resolutions = parsed.resolutions;
19809
- const currentConfig = await getStatusConfig();
19810
19855
  const oldIds = new Set(currentConfig.statuses.map((s) => s.id));
19811
19856
  const newIds = /* @__PURE__ */ new Set();
19812
- for (const s of statuses) {
19857
+ for (const s of effectiveStatuses) {
19813
19858
  if (s && typeof s === "object" && isString(s.id)) {
19814
19859
  newIds.add(s.id);
19815
19860
  }
@@ -19890,13 +19935,83 @@ function createStatusConfigRouter(projectsDir2, assignmentsDir2) {
19890
19935
  }
19891
19936
  throw err2;
19892
19937
  }
19938
+ let factsToWrite = currentConfig.facts ?? null;
19939
+ if (hasFacts) {
19940
+ const shapedFacts = [];
19941
+ if (!Array.isArray(bodyFacts)) {
19942
+ res.status(400).json({ error: "malformed-facts", message: "facts must be an array" });
19943
+ return;
19944
+ }
19945
+ for (let i = 0; i < bodyFacts.length; i++) {
19946
+ const row = bodyFacts[i];
19947
+ if (!row || typeof row !== "object") {
19948
+ res.status(400).json({ error: "malformed-facts", message: `facts[${i}] must be an object` });
19949
+ return;
19950
+ }
19951
+ const name = row.name;
19952
+ const type = row.type;
19953
+ if (typeof name !== "string" || typeof type !== "string") {
19954
+ res.status(400).json({ error: "malformed-facts", message: `facts[${i}] must have name and type strings` });
19955
+ return;
19956
+ }
19957
+ const binds = row.binds;
19958
+ const normalizedBinds = binds === void 0 ? null : binds === null ? null : typeof binds === "string" ? binds : null;
19959
+ shapedFacts.push({ name, type, binds: normalizedBinds });
19960
+ }
19961
+ const problems = validateFactDeclarations(shapedFacts);
19962
+ if (problems.length > 0) {
19963
+ res.status(400).json({ error: "invalid-facts", problems });
19964
+ return;
19965
+ }
19966
+ const currentNames = new Set((currentConfig.facts ?? []).map((f) => f.name));
19967
+ const incomingNames = new Set(shapedFacts.map((f) => f.name));
19968
+ const removedNames = [];
19969
+ for (const name of currentNames) {
19970
+ if (!incomingNames.has(name)) removedNames.push(name);
19971
+ }
19972
+ const acks = new Set(Array.isArray(factRemovalAcks) ? factRemovalAcks.map((x) => String(x)) : []);
19973
+ const unresolvedRefs = [];
19974
+ const deriveConfig = currentConfig.derive ?? null;
19975
+ if (deriveConfig !== null && removedNames.length > 0) {
19976
+ const acceptedAll = acceptFactDeclarations(normalizeFactDeclarations(currentConfig.facts));
19977
+ const fullRegistry = buildDeriveRegistry(acceptedAll);
19978
+ for (const removedName of removedNames) {
19979
+ if (acks.has(removedName)) continue;
19980
+ const acceptedWithout = acceptedAll.filter((d) => d.name !== removedName);
19981
+ const withoutRegistry = buildDeriveRegistry(acceptedWithout);
19982
+ for (let i = 0; i < deriveConfig.phaseLadder.length; i++) {
19983
+ const rung = deriveConfig.phaseLadder[i];
19984
+ if (rung.when === "*") continue;
19985
+ const before = validateDeriveCondition(rung.when, fullRegistry);
19986
+ const after = validateDeriveCondition(rung.when, withoutRegistry);
19987
+ if (before === null && after !== null) {
19988
+ unresolvedRefs.push({ factName: removedName, location: `phaseLadder[${i}]`, when: rung.when });
19989
+ }
19990
+ }
19991
+ for (let i = 0; i < deriveConfig.disposition.length; i++) {
19992
+ const rule = deriveConfig.disposition[i];
19993
+ if (rule.when === null) continue;
19994
+ const before = validateDeriveCondition(rule.when, fullRegistry);
19995
+ const after = validateDeriveCondition(rule.when, withoutRegistry);
19996
+ if (before === null && after !== null) {
19997
+ unresolvedRefs.push({ factName: removedName, location: `disposition[${i}]`, when: rule.when });
19998
+ }
19999
+ }
20000
+ }
20001
+ }
20002
+ if (unresolvedRefs.length > 0) {
20003
+ res.status(409).json({ error: "unresolved-fact-references", references: unresolvedRefs });
20004
+ return;
20005
+ }
20006
+ factsToWrite = shapedFacts;
20007
+ }
19893
20008
  try {
19894
20009
  await writeStatusConfig({
19895
- statuses,
19896
- order,
19897
- transitions,
20010
+ statuses: effectiveStatuses,
20011
+ order: effectiveOrder,
20012
+ transitions: effectiveTransitions2,
19898
20013
  derive: currentConfig.derive ?? null,
19899
- facts: currentConfig.facts ?? null
20014
+ facts: factsToWrite
19900
20015
  });
19901
20016
  } catch (err2) {
19902
20017
  console.error("Error saving status config after applying resolutions:", err2);
@@ -38367,7 +38482,9 @@ var KNOWN_FILTER_KEYS = /* @__PURE__ */ new Set([
38367
38482
  "activity",
38368
38483
  "dateRange",
38369
38484
  "search",
38370
- "query"
38485
+ "query",
38486
+ "sessionStatus",
38487
+ "agent"
38371
38488
  ]);
38372
38489
  function preserveUnknownFilterKeys(existing, built) {
38373
38490
  const out = {};
@@ -38383,6 +38500,9 @@ function mergeUpdatedConfig(existing, built, visibility) {
38383
38500
  filters: preserveUnknownFilterKeys(existing.filters, built.filters),
38384
38501
  sortField: built.sortField,
38385
38502
  sortDirection: built.sortDirection,
38503
+ // `limit` is rebuilt from `built` (session views); assignment edits carry
38504
+ // undefined here, which serializes away — so this can't leak a stale limit.
38505
+ limit: built.limit,
38386
38506
  listSectionVisibility: { collapsed: [...visibility.listSectionVisibility.collapsed] },
38387
38507
  kanbanColumnVisibility: { hidden: [...visibility.kanbanColumnVisibility.hidden] },
38388
38508
  tableColumnVisibility: { hidden: [...visibility.tableColumnVisibility.hidden] }
@@ -38407,6 +38527,10 @@ function minimizeFilters(filters, forcedProject) {
38407
38527
  if (project.length) minimal.project = project;
38408
38528
  }
38409
38529
  if (filters.activity && filters.activity !== "all") minimal.activity = filters.activity;
38530
+ const sessionStatus = toFilterValues(filters.sessionStatus);
38531
+ if (sessionStatus.length) minimal.sessionStatus = sessionStatus;
38532
+ const agent = toFilterValues(filters.agent);
38533
+ if (agent.length) minimal.agent = agent;
38410
38534
  if (filters.dateRange) minimal.dateRange = filters.dateRange;
38411
38535
  const search = filters.search?.trim();
38412
38536
  if (search) minimal.search = search;