@cleocode/contracts 2026.9.23 → 2026.9.24

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 (80) hide show
  1. package/dist/branch-lock.d.ts +9 -0
  2. package/dist/branch-lock.d.ts.map +1 -1
  3. package/dist/branch-lock.js +25 -3
  4. package/dist/branch-lock.js.map +1 -1
  5. package/dist/cloud/api.d.ts +1 -1
  6. package/dist/data-accessor.d.ts +205 -16
  7. package/dist/data-accessor.d.ts.map +1 -1
  8. package/dist/data-accessor.js.map +1 -1
  9. package/dist/decide.d.ts +110 -1
  10. package/dist/decide.d.ts.map +1 -1
  11. package/dist/decide.js +23 -0
  12. package/dist/decide.js.map +1 -1
  13. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  14. package/dist/dispatch/operations-registry.js +137 -6
  15. package/dist/dispatch/operations-registry.js.map +1 -1
  16. package/dist/doctor.d.ts +9 -0
  17. package/dist/doctor.d.ts.map +1 -1
  18. package/dist/exit-codes.d.ts +7 -0
  19. package/dist/exit-codes.d.ts.map +1 -1
  20. package/dist/exit-codes.js +7 -0
  21. package/dist/exit-codes.js.map +1 -1
  22. package/dist/index.d.ts +9 -6
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +3 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/knowledge-health.d.ts +21 -0
  27. package/dist/knowledge-health.d.ts.map +1 -1
  28. package/dist/nexus-account.d.ts +160 -0
  29. package/dist/nexus-account.d.ts.map +1 -0
  30. package/dist/nexus-account.js +68 -0
  31. package/dist/nexus-account.js.map +1 -0
  32. package/dist/operations/focus.d.ts +7 -3
  33. package/dist/operations/focus.d.ts.map +1 -1
  34. package/dist/operations/nexus-scope-map.d.ts +8 -0
  35. package/dist/operations/nexus-scope-map.d.ts.map +1 -1
  36. package/dist/operations/nexus-scope-map.js +8 -0
  37. package/dist/operations/nexus-scope-map.js.map +1 -1
  38. package/dist/operations/nexus.d.ts +265 -0
  39. package/dist/operations/nexus.d.ts.map +1 -1
  40. package/dist/operations/nexus.js +9 -1
  41. package/dist/operations/nexus.js.map +1 -1
  42. package/dist/operations/orchestrate.d.ts +6 -1
  43. package/dist/operations/orchestrate.d.ts.map +1 -1
  44. package/dist/operations/output-contracts-data.d.ts.map +1 -1
  45. package/dist/operations/output-contracts-data.js +115 -1
  46. package/dist/operations/output-contracts-data.js.map +1 -1
  47. package/dist/operations/tasks.d.ts +73 -9
  48. package/dist/operations/tasks.d.ts.map +1 -1
  49. package/dist/operations/tasks.js.map +1 -1
  50. package/dist/project-lifecycle.d.ts +6 -2
  51. package/dist/project-lifecycle.d.ts.map +1 -1
  52. package/dist/session.d.ts +19 -1
  53. package/dist/session.d.ts.map +1 -1
  54. package/dist/session.js +9 -2
  55. package/dist/session.js.map +1 -1
  56. package/dist/task.d.ts +35 -1
  57. package/dist/task.d.ts.map +1 -1
  58. package/dist/tools/score-task-priority.d.ts +1 -1
  59. package/dist/tools/score-task-priority.d.ts.map +1 -1
  60. package/package.json +2 -2
  61. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  62. package/src/branch-lock.ts +38 -3
  63. package/src/data-accessor.ts +211 -16
  64. package/src/decide.ts +127 -1
  65. package/src/dispatch/operations-registry.ts +144 -6
  66. package/src/doctor.ts +9 -0
  67. package/src/exit-codes.ts +7 -0
  68. package/src/index.ts +48 -0
  69. package/src/knowledge-health.ts +22 -0
  70. package/src/nexus-account.ts +181 -0
  71. package/src/operations/focus.ts +7 -3
  72. package/src/operations/nexus-scope-map.ts +9 -0
  73. package/src/operations/nexus.ts +281 -0
  74. package/src/operations/orchestrate.ts +6 -2
  75. package/src/operations/output-contracts-data.ts +125 -1
  76. package/src/operations/tasks.ts +78 -12
  77. package/src/project-lifecycle.ts +6 -2
  78. package/src/session.ts +20 -2
  79. package/src/task.ts +37 -1
  80. package/src/tools/score-task-priority.ts +1 -1
@@ -3072,6 +3072,14 @@ export const OPERATIONS: OperationDef[] = [
3072
3072
  description: 'taskId parameter',
3073
3073
  cli: { positional: true },
3074
3074
  },
3075
+ {
3076
+ name: 'expectedUpdatedAt',
3077
+ type: 'string',
3078
+ required: false,
3079
+ description:
3080
+ 'Optimistic-concurrency guard: fail with E_CONFLICT unless the task updatedAt still equals this value',
3081
+ cli: { flag: 'if-match' },
3082
+ },
3075
3083
  ] satisfies ParamDef[],
3076
3084
  },
3077
3085
  {
@@ -3748,7 +3756,8 @@ export const OPERATIONS: OperationDef[] = [
3748
3756
  gateway: 'mutate',
3749
3757
  domain: 'tasks',
3750
3758
  operation: 'start',
3751
- description: 'tasks.start (mutate)',
3759
+ description:
3760
+ "tasks.start (mutate) — start work and take the caller session's leased claim (E_TASK_CLAIMED when another session holds it)",
3752
3761
  tier: 0,
3753
3762
  idempotent: false,
3754
3763
  sessionRequired: false,
@@ -3761,6 +3770,20 @@ export const OPERATIONS: OperationDef[] = [
3761
3770
  description: 'taskId parameter',
3762
3771
  cli: { positional: true },
3763
3772
  },
3773
+ {
3774
+ name: 'takeOver',
3775
+ type: 'boolean',
3776
+ required: false,
3777
+ description: "Take over another session's EXPIRED claim lease (audited)",
3778
+ cli: { flag: 'take-over' },
3779
+ },
3780
+ {
3781
+ name: 'forceClaim',
3782
+ type: 'boolean',
3783
+ required: false,
3784
+ description: "Take over another session's LIVE claim lease (audited)",
3785
+ cli: { flag: 'force-claim' },
3786
+ },
3764
3787
  ] satisfies ParamDef[],
3765
3788
  },
3766
3789
  {
@@ -3869,11 +3892,12 @@ export const OPERATIONS: OperationDef[] = [
3869
3892
  gateway: 'mutate',
3870
3893
  domain: 'tasks',
3871
3894
  operation: 'claim',
3872
- description: 'tasks.claim (mutate) — claim a task by assigning it to the current session',
3895
+ description:
3896
+ "tasks.claim (mutate) — take, renew or override the caller session's leased claim on a task (never the human assignee)",
3873
3897
  tier: 0,
3874
3898
  idempotent: false,
3875
3899
  sessionRequired: true,
3876
- requiredParams: ['taskId', 'agentId'],
3900
+ requiredParams: ['taskId'],
3877
3901
  params: [
3878
3902
  {
3879
3903
  name: 'taskId',
@@ -3884,8 +3908,27 @@ export const OPERATIONS: OperationDef[] = [
3884
3908
  {
3885
3909
  name: 'agentId',
3886
3910
  type: 'string',
3887
- required: true,
3888
- description: 'Agent ID to assign the task to',
3911
+ required: false,
3912
+ description:
3913
+ "Agent ID recorded with the lease (default: the session's agent / CLEO_AGENT_ID)",
3914
+ },
3915
+ {
3916
+ name: 'renew',
3917
+ type: 'boolean',
3918
+ required: false,
3919
+ description: "Renew the caller's own lease",
3920
+ },
3921
+ {
3922
+ name: 'takeOver',
3923
+ type: 'boolean',
3924
+ required: false,
3925
+ description: "Take over another session's EXPIRED lease (audited)",
3926
+ },
3927
+ {
3928
+ name: 'forceClaim',
3929
+ type: 'boolean',
3930
+ required: false,
3931
+ description: "Take over another session's LIVE lease (audited)",
3889
3932
  },
3890
3933
  ],
3891
3934
  },
@@ -3893,7 +3936,8 @@ export const OPERATIONS: OperationDef[] = [
3893
3936
  gateway: 'mutate',
3894
3937
  domain: 'tasks',
3895
3938
  operation: 'unclaim',
3896
- description: 'tasks.unclaim (mutate) — unclaim a task by removing the current assignee',
3939
+ description:
3940
+ "tasks.unclaim (mutate) — release the caller session's claim lease (never the human assignee)",
3897
3941
  tier: 0,
3898
3942
  idempotent: false,
3899
3943
  sessionRequired: true,
@@ -3905,6 +3949,12 @@ export const OPERATIONS: OperationDef[] = [
3905
3949
  required: true,
3906
3950
  description: 'Task ID to unclaim',
3907
3951
  },
3952
+ {
3953
+ name: 'forceClaim',
3954
+ type: 'boolean',
3955
+ required: false,
3956
+ description: "Release another session's lease (audited)",
3957
+ },
3908
3958
  ],
3909
3959
  },
3910
3960
  {
@@ -4164,6 +4214,20 @@ export const OPERATIONS: OperationDef[] = [
4164
4214
  description:
4165
4215
  'When true (default), adds toTaskId as a dependency on fromTaskId so it cannot complete before the pivot resolves',
4166
4216
  },
4217
+ {
4218
+ name: 'takeOver',
4219
+ type: 'boolean',
4220
+ required: false,
4221
+ description: "Take over another session's EXPIRED claim on toTaskId (audited)",
4222
+ cli: { flag: 'take-over' },
4223
+ },
4224
+ {
4225
+ name: 'forceClaim',
4226
+ type: 'boolean',
4227
+ required: false,
4228
+ description: "Take over another session's LIVE claim on toTaskId (audited)",
4229
+ cli: { flag: 'force-claim' },
4230
+ },
4167
4231
  ],
4168
4232
  },
4169
4233
  // parallel.start and parallel.end merged into orchestrate.parallel via action param (T5615)
@@ -6907,6 +6971,80 @@ export const OPERATIONS: OperationDef[] = [
6907
6971
  params: [],
6908
6972
  },
6909
6973
 
6974
+ {
6975
+ gateway: 'query' as const,
6976
+ domain: 'nexus',
6977
+ operation: 'projects.fleet',
6978
+ description:
6979
+ 'nexus.projects.fleet (query) — fleet view: every project with its location on each device, last recorded git state (local, and remote as of the last fetch) and staleness; paged, counts first, read-only',
6980
+ tier: 1,
6981
+ idempotent: true,
6982
+ sessionRequired: false,
6983
+ requiredParams: [],
6984
+ params: [
6985
+ {
6986
+ name: 'device',
6987
+ type: 'string',
6988
+ required: false,
6989
+ description: 'Only locations on this device (device id, hostname, or current)',
6990
+ },
6991
+ {
6992
+ name: 'missing',
6993
+ type: 'boolean',
6994
+ required: false,
6995
+ description: 'Only projects with a missing location',
6996
+ },
6997
+ {
6998
+ name: 'dirty',
6999
+ type: 'boolean',
7000
+ required: false,
7001
+ description: 'Only projects with a dirty location',
7002
+ },
7003
+ {
7004
+ name: 'behind',
7005
+ type: 'boolean',
7006
+ required: false,
7007
+ description: 'Only projects with a location behind its upstream (as of the last fetch)',
7008
+ },
7009
+ {
7010
+ name: 'ahead',
7011
+ type: 'boolean',
7012
+ required: false,
7013
+ description: 'Only projects with a location ahead of its upstream (as of the last fetch)',
7014
+ },
7015
+ {
7016
+ name: 'stale',
7017
+ type: 'boolean',
7018
+ required: false,
7019
+ description: 'Only projects with a never-probed, old-probe or old-fetch location',
7020
+ },
7021
+ {
7022
+ name: 'errored',
7023
+ type: 'boolean',
7024
+ required: false,
7025
+ description: 'Only projects whose last probe recorded an error',
7026
+ },
7027
+ {
7028
+ name: 'staleAfterMs',
7029
+ type: 'number',
7030
+ required: false,
7031
+ description: 'Staleness window for probes, fetches and heartbeats (default 24h)',
7032
+ },
7033
+ {
7034
+ name: 'limit',
7035
+ type: 'number',
7036
+ required: false,
7037
+ description: 'Page size in projects (default 50, max 500; 0 = all)',
7038
+ },
7039
+ {
7040
+ name: 'offset',
7041
+ type: 'number',
7042
+ required: false,
7043
+ description: 'Projects skipped before the page',
7044
+ },
7045
+ ],
7046
+ },
7047
+
6910
7048
  {
6911
7049
  gateway: 'mutate' as const,
6912
7050
  domain: 'nexus',
package/src/doctor.ts CHANGED
@@ -880,6 +880,15 @@ export interface DbSubstrateSurveyOptions {
880
880
  * `false` → leave the corrupt DB in place; only report it.
881
881
  */
882
882
  autoQuarantine?: boolean;
883
+ /**
884
+ * Directory the survey was invoked from (T12708). Quarantine moves a
885
+ * project's live store; from a linked worktree of that project it needs
886
+ * {@link DbSubstrateSurveyOptions.confirmOwnerStore}, and it never moves a
887
+ * store inside a worktree. Defaults to the process directory.
888
+ */
889
+ cwd?: string;
890
+ /** Confirm quarantining the owning project's LIVE store from a worktree (T12708). */
891
+ confirmOwnerStore?: boolean;
883
892
  }
884
893
 
885
894
  /**
package/src/exit-codes.ts CHANGED
@@ -75,6 +75,13 @@ export enum ExitCode {
75
75
  SCOPE_CONFLICT = 32,
76
76
  SCOPE_INVALID = 33,
77
77
  TASK_NOT_IN_SCOPE = 34,
78
+ /**
79
+ * Another session holds the claim lease on the task (`cleo start`,
80
+ * `cleo claim`, spawn). LAFS code `E_TASK_CLAIMED`; the details
81
+ * (`TaskClaimedDetails`) name the holder session and agent, the lease
82
+ * expiry, and the explicit, audited override: `--take-over` for an expired
83
+ * lease, `--force-claim` for a live one. @task T12502
84
+ */
78
85
  TASK_CLAIMED = 35,
79
86
  SESSION_REQUIRED = 36,
80
87
  SESSION_CLOSE_BLOCKED = 37,
package/src/index.ts CHANGED
@@ -362,6 +362,12 @@ export type {
362
362
  QueryTasksResult,
363
363
  TaskAuditLogQuery,
364
364
  TaskAuditLogRow,
365
+ TaskClaimedDetails,
366
+ TaskClaimGuard,
367
+ TaskClaimMode,
368
+ TaskClaimRequest,
369
+ TaskConflictChange,
370
+ TaskConflictDetails,
365
371
  TaskFieldUpdates,
366
372
  TaskPopulation,
367
373
  TaskQueryFilters,
@@ -808,6 +814,7 @@ export type {
808
814
  KnowledgeEvidenceRef,
809
815
  KnowledgeFileEvidence,
810
816
  KnowledgeHealth,
817
+ KnowledgeHealthSummary,
811
818
  KnowledgeRepairAction,
812
819
  KnowledgeRepairClass,
813
820
  KnowledgeRepairFinding,
@@ -1492,6 +1499,12 @@ export type {
1492
1499
  NexusDiscoverHit,
1493
1500
  NexusDiscoverParams,
1494
1501
  NexusDiscoverResult,
1502
+ NexusFleetDevice,
1503
+ NexusFleetDeviceSummary,
1504
+ NexusFleetFlag,
1505
+ NexusFleetGitSummary,
1506
+ NexusFleetLocation,
1507
+ NexusFleetProject,
1495
1508
  NexusFlowEntry,
1496
1509
  NexusFlowsParams,
1497
1510
  NexusFlowsResult,
@@ -1541,6 +1554,8 @@ export type {
1541
1554
  NexusProjectsCleanRemoval,
1542
1555
  NexusProjectsCleanResult,
1543
1556
  NexusProjectsCleanUnreadable,
1557
+ NexusProjectsFleetParams,
1558
+ NexusProjectsFleetResult,
1544
1559
  NexusProjectsListParams,
1545
1560
  NexusProjectsListResult,
1546
1561
  NexusProjectsRegisterParams,
@@ -1621,6 +1636,7 @@ export type {
1621
1636
  NexusWhyResult,
1622
1637
  NexusWikiParams,
1623
1638
  } from './operations/nexus.js';
1639
+ export { NEXUS_FLEET_SCHEMA_VERSION } from './operations/nexus.js';
1624
1640
  // === Nexus Scope Contracts (T9145 + T9146) ===
1625
1641
  export type {
1626
1642
  MetaWithNexusScope,
@@ -2500,6 +2516,7 @@ export type {
2500
2516
  SeverityAttestation,
2501
2517
  SignedSeverityAttestation,
2502
2518
  Task,
2519
+ TaskClaim,
2503
2520
  TaskCreate,
2504
2521
  // T944 new axes (T9072: renamed TaskRole → TaskKind)
2505
2522
  TaskKind,
@@ -2890,6 +2907,12 @@ export {
2890
2907
  export type {
2891
2908
  ChoiceDecisionAnswer,
2892
2909
  ChoiceDecisionQuestion,
2910
+ DecideProfileConnection,
2911
+ DecideProfileListResult,
2912
+ DecideProfileProbe,
2913
+ DecideProfileSummary,
2914
+ DecideProfileUrlSource,
2915
+ DecideProviderState,
2893
2916
  DecisionAnswer,
2894
2917
  DecisionBatchItem,
2895
2918
  DecisionInstructions,
@@ -2912,6 +2935,9 @@ export type {
2912
2935
  export {
2913
2936
  choiceDecisionAnswerSchema,
2914
2937
  choiceDecisionQuestionSchema,
2938
+ DECIDE_DEFAULT_PROFILE_NAME,
2939
+ DECIDE_PROFILE_DEFAULT_URL,
2940
+ DECIDE_PROFILE_NAME_PATTERN,
2915
2941
  DECISION_OUTCOME_SOURCES,
2916
2942
  DECISION_PROVIDER_KINDS,
2917
2943
  DECISION_QUESTION_TYPES,
@@ -2926,6 +2952,7 @@ export {
2926
2952
  decisionQuestionTypeSchema,
2927
2953
  decisionRequestSchema,
2928
2954
  decisionStateSchema,
2955
+ JEV_DEFAULT_BASE_URL,
2929
2956
  JEV_MINIMUM_CAPABILITIES,
2930
2957
  LAYAHOST_BASE_URL,
2931
2958
  LAYAHOST_DEFAULT_MODEL,
@@ -2949,4 +2976,25 @@ export type {
2949
2976
  } from './decide-sites.js';
2950
2977
  export { DECISION_RUNGS, DECISION_SITE_MODES } from './decide-sites.js';
2951
2978
 
2979
+ export type {
2980
+ NexusAccountErrorCode,
2981
+ NexusAccountMe,
2982
+ NexusAccountOrganization,
2983
+ NexusAccountStatus,
2984
+ NexusAccountUser,
2985
+ NexusLoginResult,
2986
+ NexusLogoutResult,
2987
+ NexusProjectLink,
2988
+ NexusProjectLinkResult,
2989
+ NexusSessionState,
2990
+ } from './nexus-account.js';
2991
+ export {
2992
+ NEXUS_ACCOUNT_ERROR_CODES,
2993
+ NEXUS_CLI_CLIENT_ID,
2994
+ NEXUS_DEFAULT_API_URL,
2995
+ NEXUS_LOGIN_TARGET,
2996
+ nexusAccountMeSchema,
2997
+ nexusAccountOrganizationSchema,
2998
+ nexusAccountUserSchema,
2999
+ } from './nexus-account.js';
2952
3000
  export { isStorableTaskId, isTaskId, TASK_ID_REGEX, type TaskId } from './task-id.js';
@@ -258,6 +258,28 @@ export interface KnowledgeHealth {
258
258
  findings: KnowledgeRepairFinding[];
259
259
  }
260
260
 
261
+ /**
262
+ * Orientation summary of {@link KnowledgeHealth} for envelopes that already
263
+ * carry the coverage object at their top level (`briefing.show`'s
264
+ * `knowledgeCoverage`, `focus.show`'s `coverage`).
265
+ *
266
+ * The coverage object is emitted once per envelope; this summary points at it
267
+ * through {@link KnowledgeHealthSummary.coverageRef} instead of repeating it
268
+ * (T12522). Every other field keeps the {@link KnowledgeHealth} meaning. The
269
+ * full assessment, coverage included, remains available from
270
+ * `cleo doctor knowledge`.
271
+ *
272
+ * @task T12522
273
+ */
274
+ export interface KnowledgeHealthSummary extends Omit<KnowledgeHealth, 'coverage'> {
275
+ /**
276
+ * JSON Pointer, relative to the envelope's `data`, of the coverage object
277
+ * this summary describes — e.g. `/knowledgeCoverage` (briefing) or
278
+ * `/coverage` (focus). Prefix `/data` for `cleo ... --field`.
279
+ */
280
+ coverageRef: string;
281
+ }
282
+
261
283
  /** Responsibility for resolving a knowledge defect. */
262
284
  export type KnowledgeRepairClass = 'automatic' | 'agent-resolvable' | 'owner-decision';
263
285
 
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Cleo Nexus account contracts: the CLI's signed-in session with the Cleo
3
+ * Nexus API (device-code login, logout, status, project link).
4
+ *
5
+ * The API is a better-auth app: the device-code grant (RFC 8628) issues a
6
+ * session token that the bearer plugin accepts on every `/v1` route. These
7
+ * types describe what CLEO emits and persists about that session. None of
8
+ * them carries the token: results and status rows are secret-free by
9
+ * construction, so they are safe in LAFS envelopes and logs.
10
+ *
11
+ * Not part of `./cloud`: that directory mirrors the server's
12
+ * `@cleo-nexus/shared` contract one-to-one. The `/v1/account/me` subset
13
+ * below is read-only and client-side.
14
+ *
15
+ * This file is types + zod schemas + const data only (arch gate 10).
16
+ *
17
+ * @task T12712
18
+ * @epic T12322
19
+ */
20
+
21
+ import { z } from 'zod';
22
+
23
+ /** Production Cleo Nexus API origin. */
24
+ export const NEXUS_DEFAULT_API_URL = 'https://api.cleocode.dev';
25
+
26
+ /** The OAuth client id the Nexus API accepts for the device-code grant. */
27
+ export const NEXUS_CLI_CLIENT_ID = 'cleo-cli';
28
+
29
+ /**
30
+ * The reserved `cleo login` target for a Cleo Nexus account. It is checked
31
+ * before the LLM provider registry, so no provider can shadow it.
32
+ */
33
+ export const NEXUS_LOGIN_TARGET = 'nexus';
34
+
35
+ /** Stable error codes of the Nexus account flows. */
36
+ export const NEXUS_ACCOUNT_ERROR_CODES = [
37
+ 'E_NEXUS_INVALID_API_URL',
38
+ 'E_NEXUS_NOT_SIGNED_IN',
39
+ 'E_NEXUS_SESSION_EXPIRED',
40
+ 'E_NEXUS_DEVICE_CODE_START_FAILED',
41
+ 'E_NEXUS_UNTRUSTED_VERIFICATION_URI',
42
+ 'E_NEXUS_DEVICE_CODE_EXPIRED',
43
+ 'E_NEXUS_ACCESS_DENIED',
44
+ 'E_NEXUS_DEVICE_CODE_FAILED',
45
+ 'E_NEXUS_REVOKE_FAILED',
46
+ 'E_NEXUS_REQUEST_FAILED',
47
+ 'E_NEXUS_NOT_A_PROJECT',
48
+ 'E_NEXUS_INVALID_LABEL',
49
+ ] as const;
50
+
51
+ /** One of {@link NEXUS_ACCOUNT_ERROR_CODES}. */
52
+ export type NexusAccountErrorCode = (typeof NEXUS_ACCOUNT_ERROR_CODES)[number];
53
+
54
+ /** The signed-in user, as `/v1/account/me` reports it. */
55
+ export const nexusAccountUserSchema = z.object({
56
+ id: z.string(),
57
+ email: z.string(),
58
+ name: z.string().nullable().optional(),
59
+ });
60
+
61
+ /** A user's organization membership, as `/v1/account/me` reports it. */
62
+ export const nexusAccountOrganizationSchema = z.object({
63
+ id: z.string(),
64
+ name: z.string(),
65
+ slug: z.string().nullable().optional(),
66
+ role: z.string().optional(),
67
+ /** `true` for the personal organization created at sign-up. */
68
+ personal: z.boolean().optional(),
69
+ });
70
+
71
+ /**
72
+ * The subset of `GET /v1/account/me` the CLI reads. Unknown fields are
73
+ * ignored (zod strips them), so the server can grow the payload freely.
74
+ */
75
+ export const nexusAccountMeSchema = z.object({
76
+ user: nexusAccountUserSchema,
77
+ organizations: z.array(nexusAccountOrganizationSchema).default([]),
78
+ });
79
+
80
+ /** Parsed `GET /v1/account/me` subset. */
81
+ export type NexusAccountMe = z.infer<typeof nexusAccountMeSchema>;
82
+
83
+ /** A signed-in user (no secrets). */
84
+ export type NexusAccountUser = z.infer<typeof nexusAccountUserSchema>;
85
+
86
+ /** An organization reference (no secrets). */
87
+ export type NexusAccountOrganization = z.infer<typeof nexusAccountOrganizationSchema>;
88
+
89
+ /**
90
+ * Session state for one API origin.
91
+ *
92
+ * - `signed-in` — a token is stored and, when checked live, the API accepted it.
93
+ * - `unverified` — a token is stored but the API could not be reached.
94
+ * - `expired` — the API answered 401: the session expired or was revoked.
95
+ * - `not-signed-in` — no token is stored for the origin.
96
+ */
97
+ export type NexusSessionState = 'signed-in' | 'unverified' | 'expired' | 'not-signed-in';
98
+
99
+ /** Secret-free status row for `cleo status` and `cleo auth list`. */
100
+ export interface NexusAccountStatus {
101
+ /** API origin the row describes, e.g. `https://api.cleocode.dev`. */
102
+ apiUrl: string;
103
+ /** Session state. */
104
+ state: NexusSessionState;
105
+ /** Signed-in user's email, when known. */
106
+ email: string | null;
107
+ /** Primary organization (the personal one, else the first), when known. */
108
+ organization: string | null;
109
+ /** ISO time the stored token expires, when the server said so. */
110
+ expiresAt: string | null;
111
+ /** One human line: "signed in as a@b.c (Org)", "session expired", "not signed in". */
112
+ summary: string;
113
+ }
114
+
115
+ /** Result of `cleo login nexus` (never carries the token). */
116
+ export interface NexusLoginResult {
117
+ /** API origin the session belongs to. */
118
+ apiUrl: string;
119
+ /** The signed-in user, when `/v1/account/me` answered. */
120
+ user: NexusAccountUser | null;
121
+ /** Primary organization, when known. */
122
+ organization: NexusAccountOrganization | null;
123
+ /** ISO time the token expires, when the server said so. */
124
+ expiresAt: string | null;
125
+ /** Absolute path of the credential store the token was written to. */
126
+ credentialsPath: string;
127
+ /** Non-fatal problems, e.g. the account lookup failed after a good login. */
128
+ warnings: string[];
129
+ }
130
+
131
+ /** Result of `cleo logout nexus`. */
132
+ export interface NexusLogoutResult {
133
+ /** API origin the session belonged to. */
134
+ apiUrl: string;
135
+ /** `true` when a stored token existed and was deleted locally. */
136
+ removedLocally: boolean;
137
+ /**
138
+ * Server-side revocation outcome: `revoked` (the API ended the session),
139
+ * `already-invalid` (the API reported it was already gone), `failed`
140
+ * (network or server error: the local token is deleted anyway), or
141
+ * `skipped` (nothing was stored).
142
+ */
143
+ revocation: 'revoked' | 'already-invalid' | 'failed' | 'skipped';
144
+ /** Non-fatal problems. */
145
+ warnings: string[];
146
+ }
147
+
148
+ /**
149
+ * The binding of a local CLEO project to its Nexus registration, persisted in
150
+ * the project's machine-local `.cleo/nexus-link.json`, keyed by API origin.
151
+ */
152
+ export interface NexusProjectLink {
153
+ /** API origin the project is registered with. */
154
+ apiUrl: string;
155
+ /** The local CLEO project id (the tracked `.cleo/project-id`). */
156
+ localProjectId: string;
157
+ /** The project id the server returned (the server keys projects by the CLEO id). */
158
+ remoteProjectId: string;
159
+ /** Owning organization id. */
160
+ organizationId: string;
161
+ /** Plaintext display-name label the server holds, or `null` when it has none. */
162
+ label: string | null;
163
+ /** Journal stream id for the project. */
164
+ streamId: string;
165
+ /** ISO time of the last successful link. */
166
+ linkedAt: string;
167
+ }
168
+
169
+ /** Result of `cleo project link`. */
170
+ export interface NexusProjectLinkResult {
171
+ /** The persisted binding. */
172
+ link: NexusProjectLink;
173
+ /**
174
+ * `true` when the server already had this project id (HTTP 200; its label
175
+ * was updated to the one sent), `false` when this call registered it (201).
176
+ * Linking is idempotent either way.
177
+ */
178
+ alreadyLinked: boolean;
179
+ /** Absolute path of the local binding file. */
180
+ linkPath: string;
181
+ }
@@ -15,7 +15,7 @@
15
15
  import type {
16
16
  KnowledgeCoverage,
17
17
  KnowledgeDiagnostic,
18
- KnowledgeHealth,
18
+ KnowledgeHealthSummary,
19
19
  } from '../knowledge-health.js';
20
20
  import type { MviDigest } from '../mvi.js';
21
21
  import type { AttentionScopeKind } from './attention.js';
@@ -195,8 +195,12 @@ export interface FocusBrainContext {
195
195
  export interface FocusShowResult {
196
196
  /** Assessed graph coverage; absence on legacy responses is not healthy coverage. */
197
197
  coverage?: KnowledgeCoverage;
198
- /** Bounded deterministic maintenance and sourced repair findings for the foreground caller. */
199
- knowledgeHealth?: KnowledgeHealth;
198
+ /**
199
+ * Bounded deterministic maintenance and sourced repair findings for the
200
+ * foreground caller. Its coverage is not repeated: `coverageRef` is
201
+ * `/coverage`, the single coverage object of this result (T12522).
202
+ */
203
+ knowledgeHealth?: KnowledgeHealthSummary;
200
204
  /** Failed auxiliary reads are distinct from successfully empty sources. */
201
205
  sourceDiagnostics?: Record<string, KnowledgeDiagnostic>;
202
206
  /** Core identity of the focused entity. */
@@ -473,6 +473,15 @@ export const NEXUS_SCOPE_MAP = {
473
473
  stores: ['nexus-registry'],
474
474
  requiresProject: false,
475
475
  },
476
+ 'projects.fleet': {
477
+ op: 'projects.fleet',
478
+ description:
479
+ 'Fleet view: every project, where it lives on each device, and its last recorded git state (read-only).',
480
+ scope: 'global',
481
+ effect: 'read',
482
+ stores: ['nexus-registry'],
483
+ requiresProject: false,
484
+ },
476
485
  'projects.register': {
477
486
  op: 'projects.register',
478
487
  description: 'Register a project (alias: register).',