@adhdev/mesh-shared 1.0.82-rc.23 → 1.0.82-rc.25

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.
@@ -62,11 +62,128 @@ export declare const ASSISTANT_WRITE_TOOLS: readonly AssistantTool[];
62
62
  export declare const ASSISTANT_OWNER_VERBS: readonly AssistantVerb[];
63
63
  /** The only assistant verbs accepted while an idle review turn is open (§4.10.7). */
64
64
  export declare const ASSISTANT_REVIEW_TURN_VERBS: readonly AssistantVerb[];
65
- /**
66
- * Env var the daemon sets on the assistant CLI's process (§4.5), read by the
65
+ /** Env var the daemon sets on the assistant CLI's process (§4.5), read by the
67
66
  * assistant MCP server and forwarded on every verb as
68
67
  * `ASSISTANT_SESSION_ID_ARG`. Routing / ownership only — never an auth gate.
69
68
  */
70
69
  export declare const ASSISTANT_SESSION_ID_ENV = "ADHDEV_ASSISTANT_SESSION_ID";
71
70
  /** Request arg carrying the calling assistant session id. */
72
71
  export declare const ASSISTANT_SESSION_ID_ARG = "assistantSessionId";
72
+ /**
73
+ * Conceptual single-assistant slot in the verified account namespace.
74
+ * Fixed to `'personal'` — one logical assistant per account with one
75
+ * owner-selected active execution daemon binding per project ACL.
76
+ */
77
+ export declare const ASSISTANT_ACCOUNT_SLOT: "personal";
78
+ export type AssistantAccountSlot = typeof ASSISTANT_ACCOUNT_SLOT;
79
+ /**
80
+ * Daemon commands for remote project discovery, host ACL grant/revoke, and
81
+ * assistant-side follow/unfollow. Registered outside `ASSISTANT_VERB` so the
82
+ * assistant MCP server tool/verb parity table remains untouched.
83
+ */
84
+ export declare const ASSISTANT_REMOTE_PROJECT_COMMAND: "assistant_remote_project";
85
+ export declare const ASSISTANT_REMOTE_PROJECT_CATALOG_COMMAND: "assistant_remote_project_catalog";
86
+ export declare const ASSISTANT_REMOTE_PROJECT_ACCESS_COMMAND: "assistant_remote_project_access";
87
+ export declare const ASSISTANT_REMOTE_FOLLOW_COMMAND: "assistant_remote_follow";
88
+ /**
89
+ * Narrow operations a non-roster follower deputy is permitted to invoke on
90
+ * `assistant_remote_project` when the host's `assistantRemoteAccess` ACL is active.
91
+ * `note`, `terminal`, and `inventory` remain strictly roster-member-only.
92
+ */
93
+ export declare const ASSISTANT_FOLLOWER_ALLOWED_OPS: readonly ["status", "read", "poll", "report", "send"];
94
+ export type AssistantFollowerAllowedOp = typeof ASSISTANT_FOLLOWER_ALLOWED_OPS[number];
95
+ export declare function isAssistantFollowerAllowedOp(value: unknown): value is AssistantFollowerAllowedOp;
96
+ /**
97
+ * Host-authoritative narrow project ACL persisted on the hosting daemon's
98
+ * `LocalMeshEntry` in `meshes.json`. Revocation increments `grantRevision`
99
+ * and retains a `'revoked'` tombstone so stale follower caches cannot replay.
100
+ */
101
+ export interface AssistantRemoteAccessBinding {
102
+ hostDaemonId: string;
103
+ meshId: string;
104
+ accountSlot: AssistantAccountSlot;
105
+ activeDaemonId: string;
106
+ bindingRevision: number;
107
+ grantRevision: number;
108
+ status: 'active' | 'revoked';
109
+ allowedOps: readonly AssistantFollowerAllowedOp[];
110
+ grantedAt: number;
111
+ revokedAt?: number;
112
+ }
113
+ /**
114
+ * Durable owner-binding receipt persisted in the assistant daemon's
115
+ * `assistant.json`. Survives ordinary daemon restarts and session/provider
116
+ * replacements (`bindSession`), while missing/corrupt/mismatched receipts
117
+ * fail closed until owner reconciliation.
118
+ */
119
+ export interface AssistantOwnerBindingReceipt {
120
+ accountSlot: AssistantAccountSlot;
121
+ daemonId: string;
122
+ bindingRevision: number;
123
+ boundAt: number | string;
124
+ }
125
+ export type FollowedRemoteProjectState = 'followed' | 'unfollowed' | 'revoked';
126
+ export type RemoteAccessState = 'not_granted' | 'granted_unfollowed' | 'followed' | 'revoked' | 'host_unreachable' | 'binding_mismatch';
127
+ /**
128
+ * Assistant-local followed remote project reference persisted in `assistant.json`.
129
+ * Never injected into `meshes.json`, `mesh-host-records.json`, `mesh-node-state-push.json`,
130
+ * or `inlineMeshCache`.
131
+ */
132
+ export interface FollowedRemoteProjectEntry {
133
+ /** Canonical key: `${canonicalDaemonId(hostDaemonId)}::${meshId}` */
134
+ key: string;
135
+ hostDaemonId: string;
136
+ hostLabel?: string;
137
+ meshId: string;
138
+ name: string;
139
+ slug: string;
140
+ sanitizedRepoIdentity: string | null;
141
+ repoIdentity?: string;
142
+ accountSlot: AssistantAccountSlot;
143
+ bindingRevision: number;
144
+ grantRevision: number;
145
+ state: FollowedRemoteProjectState;
146
+ followedAt: number | string;
147
+ updatedAt: number | string;
148
+ unfollowedAt?: number | string;
149
+ revokedAt?: number | string;
150
+ lastCursor?: string | null;
151
+ coordinatorSessionId?: string | null;
152
+ lastErrorCode?: string;
153
+ lastObserved?: {
154
+ observedAt: number | string;
155
+ coordinatorState?: string;
156
+ coordinatorMissing?: boolean;
157
+ coordinatorPresent?: boolean;
158
+ lastHeadline?: string;
159
+ summary?: string;
160
+ };
161
+ }
162
+ /**
163
+ * Content-free canonical catalog metadata returned by `assistant_remote_project_catalog`
164
+ * for meshes actually hosted on the queried remote daemon.
165
+ */
166
+ export interface RemoteProjectCatalogItem {
167
+ hostDaemonId: string;
168
+ meshId: string;
169
+ name: string;
170
+ sanitizedRepoIdentity: string | null;
171
+ coordinatorState?: 'none' | 'idle' | 'working' | 'waiting' | string;
172
+ capabilities: {
173
+ status: true;
174
+ read: true;
175
+ poll: true;
176
+ report: true;
177
+ send: true;
178
+ coordinatorPresent?: boolean;
179
+ };
180
+ observedAt: number | string;
181
+ }
182
+ /** Build the canonical `(hostDaemonId, meshId)` key used across assistant follow state. */
183
+ export declare function followedRemoteProjectKey(hostDaemonId: string, meshId: string): string;
184
+ /**
185
+ * Sanitize a repository identity or remote URL into a content-free `host/path`
186
+ * identifier suitable for remote discovery catalogs. Strips credentials, query
187
+ * strings, fragments, and `.git` suffixes, and rejects local filesystem paths.
188
+ */
189
+ export declare function sanitizeRemoteRepoIdentity(raw: string | null | undefined): string | null;
package/dist/index.js CHANGED
@@ -20,7 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ ASSISTANT_ACCOUNT_SLOT: () => ASSISTANT_ACCOUNT_SLOT,
24
+ ASSISTANT_FOLLOWER_ALLOWED_OPS: () => ASSISTANT_FOLLOWER_ALLOWED_OPS,
23
25
  ASSISTANT_OWNER_VERBS: () => ASSISTANT_OWNER_VERBS,
26
+ ASSISTANT_REMOTE_FOLLOW_COMMAND: () => ASSISTANT_REMOTE_FOLLOW_COMMAND,
27
+ ASSISTANT_REMOTE_PROJECT_ACCESS_COMMAND: () => ASSISTANT_REMOTE_PROJECT_ACCESS_COMMAND,
28
+ ASSISTANT_REMOTE_PROJECT_CATALOG_COMMAND: () => ASSISTANT_REMOTE_PROJECT_CATALOG_COMMAND,
29
+ ASSISTANT_REMOTE_PROJECT_COMMAND: () => ASSISTANT_REMOTE_PROJECT_COMMAND,
24
30
  ASSISTANT_REVIEW_TURN_VERBS: () => ASSISTANT_REVIEW_TURN_VERBS,
25
31
  ASSISTANT_SESSION_ID_ARG: () => ASSISTANT_SESSION_ID_ARG,
26
32
  ASSISTANT_SESSION_ID_ENV: () => ASSISTANT_SESSION_ID_ENV,
@@ -229,11 +235,13 @@ __export(index_exports, {
229
235
  expandDaemonIdForms: () => expandDaemonIdForms,
230
236
  findOwnershipConflicts: () => findOwnershipConflicts,
231
237
  foldKeyedDoc: () => foldKeyedDoc,
238
+ followedRemoteProjectKey: () => followedRemoteProjectKey,
232
239
  formatQuotaAccount: () => formatQuotaAccount,
233
240
  hasGitStatusEvidence: () => hasGitStatusEvidence,
234
241
  hasWorkerProtocolFooter: () => hasWorkerProtocolFooter,
235
242
  interpolateString: () => interpolateString,
236
243
  isActionableProviderApproval: () => isActionableProviderApproval,
244
+ isAssistantFollowerAllowedOp: () => isAssistantFollowerAllowedOp,
237
245
  isAssistantTool: () => isAssistantTool,
238
246
  isBlockedStatus: () => isBlockedStatus,
239
247
  isBusyStatus: () => isBusyStatus,
@@ -357,6 +365,7 @@ __export(index_exports, {
357
365
  retiredMeshToolError: () => retiredMeshToolError,
358
366
  sanitizeModelIdentifier: () => sanitizeModelIdentifier,
359
367
  sanitizeRefusalCode: () => sanitizeRefusalCode,
368
+ sanitizeRemoteRepoIdentity: () => sanitizeRemoteRepoIdentity,
360
369
  scoreGitStatusCandidate: () => scoreGitStatusCandidate,
361
370
  scoreGitUpstreamFreshness: () => scoreGitUpstreamFreshness,
362
371
  sessionIdsEquivalent: () => sessionIdsEquivalent,
@@ -1164,7 +1173,8 @@ var CANONICAL_MESH_TOOL_NAMES = [
1164
1173
  "mesh_review_inbox",
1165
1174
  "mesh_node_slots",
1166
1175
  "driver_list",
1167
- "driver_describe"
1176
+ "driver_describe",
1177
+ "artifact_list"
1168
1178
  ];
1169
1179
  var CANONICAL_MESH_TOOL_COUNT = CANONICAL_MESH_TOOL_NAMES.length;
1170
1180
  var RETIRED_MESH_TOOLS = {
@@ -2048,7 +2058,8 @@ var WORKER_TOOLS = [
2048
2058
  "git_diff",
2049
2059
  "driver_list",
2050
2060
  "driver_describe",
2051
- "driver_invoke"
2061
+ "driver_invoke",
2062
+ "artifact_register"
2052
2063
  ];
2053
2064
  var WORKER_TOOL_SET = new Set(WORKER_TOOLS);
2054
2065
  function isWorkerTool(value) {
@@ -2224,6 +2235,63 @@ var ASSISTANT_REVIEW_TURN_VERBS = [
2224
2235
  ];
2225
2236
  var ASSISTANT_SESSION_ID_ENV = "ADHDEV_ASSISTANT_SESSION_ID";
2226
2237
  var ASSISTANT_SESSION_ID_ARG = "assistantSessionId";
2238
+ var ASSISTANT_ACCOUNT_SLOT = "personal";
2239
+ var ASSISTANT_REMOTE_PROJECT_COMMAND = "assistant_remote_project";
2240
+ var ASSISTANT_REMOTE_PROJECT_CATALOG_COMMAND = "assistant_remote_project_catalog";
2241
+ var ASSISTANT_REMOTE_PROJECT_ACCESS_COMMAND = "assistant_remote_project_access";
2242
+ var ASSISTANT_REMOTE_FOLLOW_COMMAND = "assistant_remote_follow";
2243
+ var ASSISTANT_FOLLOWER_ALLOWED_OPS = ["status", "read", "poll", "report", "send"];
2244
+ var ASSISTANT_FOLLOWER_ALLOWED_OP_SET = new Set(ASSISTANT_FOLLOWER_ALLOWED_OPS);
2245
+ function isAssistantFollowerAllowedOp(value) {
2246
+ return typeof value === "string" && ASSISTANT_FOLLOWER_ALLOWED_OP_SET.has(value);
2247
+ }
2248
+ function followedRemoteProjectKey(hostDaemonId, meshId) {
2249
+ const canonHost = canonicalDaemonId(hostDaemonId) ?? hostDaemonId.trim();
2250
+ return `${canonHost}::${meshId.trim()}`;
2251
+ }
2252
+ function sanitizeRemoteRepoIdentity(raw) {
2253
+ if (typeof raw !== "string") return null;
2254
+ const trimmed = raw.trim();
2255
+ if (!trimmed) return null;
2256
+ if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("~/") || trimmed.startsWith("\\") || /^[a-zA-Z]:[\\/]/.test(trimmed) || /^file:/i.test(trimmed)) {
2257
+ return null;
2258
+ }
2259
+ const withoutQuery = trimmed.replace(/[?#].*$/, "").replace(/\/+$/, "");
2260
+ if (!withoutQuery) return null;
2261
+ let host = "";
2262
+ let repoPath = "";
2263
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(withoutQuery)) {
2264
+ try {
2265
+ const url = new URL(withoutQuery);
2266
+ if (url.protocol === "file:") return null;
2267
+ host = url.hostname.toLowerCase();
2268
+ repoPath = decodeURIComponent(url.pathname).replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
2269
+ } catch {
2270
+ return null;
2271
+ }
2272
+ } else {
2273
+ const scpMatch = withoutQuery.match(/^(?:[^@/:]+@)?(\[[^\]]+\]|[^/:]+):(.+)$/);
2274
+ if (scpMatch) {
2275
+ host = scpMatch[1].replace(/^\[|\]$/g, "").toLowerCase();
2276
+ repoPath = scpMatch[2].replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
2277
+ } else {
2278
+ const slash = withoutQuery.indexOf("/");
2279
+ if (slash > 0) {
2280
+ const rawHost = withoutQuery.slice(0, slash);
2281
+ const atIdx = rawHost.lastIndexOf("@");
2282
+ host = (atIdx >= 0 ? rawHost.slice(atIdx + 1) : rawHost).toLowerCase();
2283
+ repoPath = withoutQuery.slice(slash + 1).replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
2284
+ }
2285
+ }
2286
+ }
2287
+ if (!host || !repoPath) return null;
2288
+ if (!host.includes(".") && host !== "localhost") return null;
2289
+ if (/[\s@:\\]/.test(host)) return null;
2290
+ const segments = repoPath.split("/").filter(Boolean);
2291
+ if (segments.length === 0) return null;
2292
+ if (segments.some((seg) => seg === "." || seg === ".." || /[\s@:\\]/.test(seg))) return null;
2293
+ return `${host}/${segments.join("/")}`.slice(0, 200);
2294
+ }
2227
2295
 
2228
2296
  // src/turn-evidence.ts
2229
2297
  var MESH_TOPIC_PROTOCOL_VERSION = 2;
@@ -4050,7 +4118,13 @@ function isProviderQuestionBinding(value) {
4050
4118
  }
4051
4119
  // Annotate the CommonJS export names for ESM import in node:
4052
4120
  0 && (module.exports = {
4121
+ ASSISTANT_ACCOUNT_SLOT,
4122
+ ASSISTANT_FOLLOWER_ALLOWED_OPS,
4053
4123
  ASSISTANT_OWNER_VERBS,
4124
+ ASSISTANT_REMOTE_FOLLOW_COMMAND,
4125
+ ASSISTANT_REMOTE_PROJECT_ACCESS_COMMAND,
4126
+ ASSISTANT_REMOTE_PROJECT_CATALOG_COMMAND,
4127
+ ASSISTANT_REMOTE_PROJECT_COMMAND,
4054
4128
  ASSISTANT_REVIEW_TURN_VERBS,
4055
4129
  ASSISTANT_SESSION_ID_ARG,
4056
4130
  ASSISTANT_SESSION_ID_ENV,
@@ -4259,11 +4333,13 @@ function isProviderQuestionBinding(value) {
4259
4333
  expandDaemonIdForms,
4260
4334
  findOwnershipConflicts,
4261
4335
  foldKeyedDoc,
4336
+ followedRemoteProjectKey,
4262
4337
  formatQuotaAccount,
4263
4338
  hasGitStatusEvidence,
4264
4339
  hasWorkerProtocolFooter,
4265
4340
  interpolateString,
4266
4341
  isActionableProviderApproval,
4342
+ isAssistantFollowerAllowedOp,
4267
4343
  isAssistantTool,
4268
4344
  isBlockedStatus,
4269
4345
  isBusyStatus,
@@ -4387,6 +4463,7 @@ function isProviderQuestionBinding(value) {
4387
4463
  retiredMeshToolError,
4388
4464
  sanitizeModelIdentifier,
4389
4465
  sanitizeRefusalCode,
4466
+ sanitizeRemoteRepoIdentity,
4390
4467
  scoreGitStatusCandidate,
4391
4468
  scoreGitUpstreamFreshness,
4392
4469
  sessionIdsEquivalent,