@indigoai-us/hq-cli 5.125.0 → 5.126.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.
- package/CHANGELOG.md +118 -0
- package/assets/bot-workers/setup/skills/joining-a-team.md +125 -0
- package/assets/bot-workers/setup/skills/your-business.md +270 -0
- package/assets/bot-workers/setup/worker.yaml +189 -58
- package/dist/command-catalog.generated.d.ts +407 -4
- package/dist/command-catalog.generated.js +522 -4
- package/dist/command-registration-plan.d.ts +6 -0
- package/dist/command-registration-plan.js +1 -0
- package/dist/commands/bot.d.ts +10 -0
- package/dist/commands/bot.js +6 -0
- package/dist/commands/lanes.d.ts +75 -0
- package/dist/commands/lanes.js +2032 -0
- package/dist/commands/mesh.js +37 -9
- package/dist/commands/onboard-create-identity.d.ts +26 -0
- package/dist/commands/onboard-create-identity.js +23 -0
- package/dist/commands/onboard.js +13 -4
- package/dist/lib/bot/progress.d.ts +6 -0
- package/dist/lib/bot/progress.js +16 -0
- package/dist/lib/bot/runtime/grok.d.ts +30 -7
- package/dist/lib/bot/runtime/grok.js +86 -8
- package/dist/lib/bot/runtime/index.js +15 -8
- package/dist/lib/bot/runtime/messages-stream.d.ts +12 -3
- package/dist/lib/bot/runtime/messages-stream.js +49 -3
- package/dist/lib/lanes/accounts.d.ts +43 -0
- package/dist/lib/lanes/accounts.js +128 -0
- package/dist/lib/lanes/config.d.ts +56 -0
- package/dist/lib/lanes/config.js +351 -0
- package/dist/lib/lanes/errors.d.ts +39 -0
- package/dist/lib/lanes/errors.js +72 -0
- package/dist/lib/lanes/index.d.ts +19 -0
- package/dist/lib/lanes/index.js +12 -0
- package/dist/lib/lanes/liveness.d.ts +89 -0
- package/dist/lib/lanes/liveness.js +273 -0
- package/dist/lib/lanes/registry.d.ts +103 -0
- package/dist/lib/lanes/registry.js +247 -0
- package/dist/lib/lanes/store.d.ts +64 -0
- package/dist/lib/lanes/store.js +627 -0
- package/dist/lib/lanes/throttle.d.ts +89 -0
- package/dist/lib/lanes/throttle.js +297 -0
- package/dist/lib/lanes/types.d.ts +193 -0
- package/dist/lib/lanes/types.js +217 -0
- package/dist/lib/mesh/live/daemon/config.d.ts +27 -0
- package/dist/lib/mesh/live/daemon/config.js +75 -0
- package/dist/lib/mesh/live/daemon/index.d.ts +4 -3
- package/dist/lib/mesh/live/daemon/index.js +3 -3
- package/dist/lib/mesh/live/daemon/install.d.ts +143 -1
- package/dist/lib/mesh/live/daemon/install.js +444 -2
- package/dist/lib/mesh/live/daemon/paths.d.ts +2 -0
- package/dist/lib/mesh/live/daemon/paths.js +2 -0
- package/dist/lib/scan-packages/index.js +17 -12
- package/dist/lib/work-context/config.d.ts +20 -0
- package/dist/lib/work-context/config.js +26 -5
- package/dist/lib/work-context/reconcile.js +28 -7
- package/dist/lib/work-context/windows-acl.d.ts +123 -0
- package/dist/lib/work-context/windows-acl.js +425 -0
- package/dist/utils/pack-contributions.js +5 -4
- package/package.json +1 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HQ worker resolution for lanes (spec §5.3).
|
|
3
|
+
*
|
|
4
|
+
* Tenancy derives from the registry entry's `path:` prefix, never from the
|
|
5
|
+
* optional `company:` field. A company path with a missing or contradictory
|
|
6
|
+
* `company:` is `worker_tenancy_unresolved`, never a global worker.
|
|
7
|
+
*
|
|
8
|
+
* The registry does not index personal/workers/ for this command. Naming a
|
|
9
|
+
* personal worker fails with a clear error and does not fall through to a
|
|
10
|
+
* same-named entry in another tree.
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import { parseUserYaml } from "../../utils/user-yaml-error.js";
|
|
15
|
+
import { WorkerResolutionError } from "./errors.js";
|
|
16
|
+
export const WORKER_REGISTRY_REL = path.join("core", "workers", "registry.yaml");
|
|
17
|
+
function defaultRegistryDeps() {
|
|
18
|
+
return {
|
|
19
|
+
existsSync: (p) => fs.existsSync(p),
|
|
20
|
+
readFileSync: (p, encoding) => fs.readFileSync(p, encoding),
|
|
21
|
+
statSync: (p) => fs.statSync(p),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function isPlainObject(value) {
|
|
25
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
26
|
+
}
|
|
27
|
+
export function registryPath(hqRoot) {
|
|
28
|
+
return path.join(hqRoot, WORKER_REGISTRY_REL);
|
|
29
|
+
}
|
|
30
|
+
export function personalWorkerDir(hqRoot, workerId) {
|
|
31
|
+
return path.join(hqRoot, "personal", "workers", workerId);
|
|
32
|
+
}
|
|
33
|
+
function isExistingPath(p, deps) {
|
|
34
|
+
try {
|
|
35
|
+
const st = deps.statSync(p);
|
|
36
|
+
return st.isFile() || st.isDirectory();
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
const code = err.code;
|
|
40
|
+
if (code === "ENOENT")
|
|
41
|
+
return false;
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function personalWorkerExists(hqRoot, workerId, deps = defaultRegistryDeps()) {
|
|
46
|
+
const yamlPath = path.join(personalWorkerDir(hqRoot, workerId), "worker.yaml");
|
|
47
|
+
try {
|
|
48
|
+
return deps.statSync(yamlPath).isFile();
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
const code = err.code;
|
|
52
|
+
if (code === "ENOENT")
|
|
53
|
+
return false;
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function readWorkerRegistryFile(hqRoot, deps = defaultRegistryDeps()) {
|
|
58
|
+
const filePath = registryPath(hqRoot);
|
|
59
|
+
if (!deps.existsSync(filePath)) {
|
|
60
|
+
throw new WorkerResolutionError("worker_registry_unreadable", `Worker registry missing at ${filePath}`, { path: filePath });
|
|
61
|
+
}
|
|
62
|
+
let text;
|
|
63
|
+
try {
|
|
64
|
+
text = deps.readFileSync(filePath, "utf8");
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
throw new WorkerResolutionError("worker_registry_unreadable", `Failed to read ${filePath}: ${err instanceof Error ? err.message : String(err)}`, { path: filePath });
|
|
68
|
+
}
|
|
69
|
+
const doc = parseUserYaml(text, filePath);
|
|
70
|
+
const workers = doc?.workers;
|
|
71
|
+
if (!Array.isArray(workers)) {
|
|
72
|
+
throw new WorkerResolutionError("worker_registry_unreadable", `Worker registry workers key is missing or not an array at ${filePath}`, { path: filePath });
|
|
73
|
+
}
|
|
74
|
+
const entries = [];
|
|
75
|
+
for (let i = 0; i < workers.length; i += 1) {
|
|
76
|
+
const item = workers[i];
|
|
77
|
+
if (!isPlainObject(item)) {
|
|
78
|
+
process.stderr.write(`lanes registry: skipping malformed worker entry in ${filePath} at index ${i}: not an object (type=${typeof item})\n`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (typeof item.id !== "string" ||
|
|
82
|
+
item.id.length === 0 ||
|
|
83
|
+
typeof item.path !== "string" ||
|
|
84
|
+
item.path.length === 0) {
|
|
85
|
+
process.stderr.write(`lanes registry: skipping malformed worker entry in ${filePath} at index ${i}: missing id or path (id=${JSON.stringify(item.id)} path=${JSON.stringify(item.path)})\n`);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
entries.push({
|
|
89
|
+
id: item.id,
|
|
90
|
+
path: item.path,
|
|
91
|
+
type: typeof item.type === "string" ? item.type : "",
|
|
92
|
+
visibility: typeof item.visibility === "string" ? item.visibility : undefined,
|
|
93
|
+
company: typeof item.company === "string" ? item.company : undefined,
|
|
94
|
+
team: typeof item.team === "string" ? item.team : undefined,
|
|
95
|
+
status: typeof item.status === "string" ? item.status : undefined,
|
|
96
|
+
description: typeof item.description === "string" ? item.description : undefined,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return entries;
|
|
100
|
+
}
|
|
101
|
+
const COMPANY_PATH = /^companies\/([^/]+)\//;
|
|
102
|
+
const PERSONAL_PATH = /^(?:personal\/workers\/)/;
|
|
103
|
+
const CORE_PATH = /^(?:core\/workers\/)/;
|
|
104
|
+
/**
|
|
105
|
+
* Tenancy from path, not from `company:`. A company path whose field is
|
|
106
|
+
* missing or disagrees is unresolved — never global.
|
|
107
|
+
*/
|
|
108
|
+
export function tenancyFromRegistryPath(entry) {
|
|
109
|
+
const normalized = entry.path.replaceAll("\\", "/");
|
|
110
|
+
if (PERSONAL_PATH.test(normalized)) {
|
|
111
|
+
return { kind: "personal" };
|
|
112
|
+
}
|
|
113
|
+
const companyMatch = COMPANY_PATH.exec(normalized);
|
|
114
|
+
if (companyMatch) {
|
|
115
|
+
const pathSlug = companyMatch[1] ?? "";
|
|
116
|
+
if (!pathSlug) {
|
|
117
|
+
return { kind: "unresolved", pathSlug, field: entry.company };
|
|
118
|
+
}
|
|
119
|
+
const field = entry.company?.trim() ?? "";
|
|
120
|
+
if (!field || field !== pathSlug) {
|
|
121
|
+
return { kind: "unresolved", pathSlug, field: field || undefined };
|
|
122
|
+
}
|
|
123
|
+
return { kind: "company", slug: pathSlug };
|
|
124
|
+
}
|
|
125
|
+
if (CORE_PATH.test(normalized) || normalized.startsWith("core/")) {
|
|
126
|
+
return { kind: "global" };
|
|
127
|
+
}
|
|
128
|
+
// Unknown tree: do not treat as global.
|
|
129
|
+
return { kind: "unresolved", pathSlug: "", field: entry.company };
|
|
130
|
+
}
|
|
131
|
+
function loadWorkerYaml(hqRoot, relativeDir, deps) {
|
|
132
|
+
const dir = path.join(hqRoot, relativeDir);
|
|
133
|
+
const filePath = path.join(dir, "worker.yaml");
|
|
134
|
+
if (!deps.existsSync(filePath)) {
|
|
135
|
+
throw new WorkerResolutionError("worker_not_found", `worker.yaml missing at ${filePath}`, { path: filePath });
|
|
136
|
+
}
|
|
137
|
+
const text = deps.readFileSync(filePath, "utf8");
|
|
138
|
+
const doc = parseUserYaml(text, filePath);
|
|
139
|
+
if (doc === null || typeof doc !== "object" || Array.isArray(doc)) {
|
|
140
|
+
return {};
|
|
141
|
+
}
|
|
142
|
+
return doc;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Resolve context.base against HQ root, then core/, then the worker directory.
|
|
146
|
+
* Entries that resolve nowhere are dropped, not passed on.
|
|
147
|
+
*/
|
|
148
|
+
export function resolveContextBasePaths(hqRoot, workerDir, bases, deps = defaultRegistryDeps()) {
|
|
149
|
+
if (!bases || bases.length === 0)
|
|
150
|
+
return [];
|
|
151
|
+
const resolved = [];
|
|
152
|
+
for (const entry of bases) {
|
|
153
|
+
if (typeof entry !== "string" || entry.length === 0)
|
|
154
|
+
continue;
|
|
155
|
+
const candidates = [
|
|
156
|
+
path.isAbsolute(entry) ? entry : path.join(hqRoot, entry),
|
|
157
|
+
path.join(hqRoot, "core", entry),
|
|
158
|
+
path.join(workerDir, entry),
|
|
159
|
+
];
|
|
160
|
+
const hit = candidates.find((candidate) => isExistingPath(candidate, deps));
|
|
161
|
+
if (hit)
|
|
162
|
+
resolved.push(hit);
|
|
163
|
+
}
|
|
164
|
+
return resolved;
|
|
165
|
+
}
|
|
166
|
+
export function resolveSkillFiles(workerDir, skills, deps = defaultRegistryDeps()) {
|
|
167
|
+
if (!skills)
|
|
168
|
+
return [];
|
|
169
|
+
const files = [];
|
|
170
|
+
for (const skill of skills) {
|
|
171
|
+
if (!skill || typeof skill.file !== "string" || skill.file.length === 0) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const candidate = path.isAbsolute(skill.file)
|
|
175
|
+
? skill.file
|
|
176
|
+
: path.join(workerDir, skill.file);
|
|
177
|
+
if (isExistingPath(candidate, deps))
|
|
178
|
+
files.push(candidate);
|
|
179
|
+
}
|
|
180
|
+
return files;
|
|
181
|
+
}
|
|
182
|
+
function humanCheckpointsOf(yaml) {
|
|
183
|
+
const raw = yaml.verification?.human_checkpoints;
|
|
184
|
+
if (!Array.isArray(raw))
|
|
185
|
+
return [];
|
|
186
|
+
return raw.filter((item) => typeof item === "string");
|
|
187
|
+
}
|
|
188
|
+
export function resolveLaneWorker(hqRoot, workerId, laneCompany, deps = defaultRegistryDeps()) {
|
|
189
|
+
if (personalWorkerExists(hqRoot, workerId, deps)) {
|
|
190
|
+
throw new WorkerResolutionError("personal_worker_unindexed", `Worker ${JSON.stringify(workerId)} lives under personal/workers/ which the registry does not index; --worker cannot resolve personal workers`, {
|
|
191
|
+
worker: workerId,
|
|
192
|
+
path: path.join("personal", "workers", workerId).replaceAll("\\", "/"),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
const entries = readWorkerRegistryFile(hqRoot, deps);
|
|
196
|
+
const entry = entries.find((item) => item.id === workerId);
|
|
197
|
+
if (!entry) {
|
|
198
|
+
throw new WorkerResolutionError("worker_not_found", `Worker ${JSON.stringify(workerId)} is not in core/workers/registry.yaml`, { worker: workerId });
|
|
199
|
+
}
|
|
200
|
+
const tenancy = tenancyFromRegistryPath(entry);
|
|
201
|
+
if (tenancy.kind === "personal") {
|
|
202
|
+
throw new WorkerResolutionError("personal_worker_unindexed", `Worker ${JSON.stringify(workerId)} is registered under personal/workers/ which lanes do not admit`, { worker: workerId, path: entry.path });
|
|
203
|
+
}
|
|
204
|
+
if (tenancy.kind === "unresolved") {
|
|
205
|
+
throw new WorkerResolutionError("worker_tenancy_unresolved", `Worker ${JSON.stringify(workerId)} has a company path whose company field is missing or contradicts the path`, {
|
|
206
|
+
worker: workerId,
|
|
207
|
+
path: entry.path,
|
|
208
|
+
path_slug: tenancy.pathSlug,
|
|
209
|
+
company_field: tenancy.field ?? null,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
if (tenancy.kind === "company") {
|
|
213
|
+
if (!laneCompany || laneCompany !== tenancy.slug) {
|
|
214
|
+
throw new WorkerResolutionError("worker_not_available", `Worker ${JSON.stringify(workerId)} belongs to company ${tenancy.slug} and is not available to ${laneCompany ?? "(none)"}`, {
|
|
215
|
+
worker: workerId,
|
|
216
|
+
worker_company: tenancy.slug,
|
|
217
|
+
lane_company: laneCompany ?? null,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const status = entry.status && entry.status.length > 0 ? entry.status : "active";
|
|
222
|
+
if (status !== "active") {
|
|
223
|
+
throw new WorkerResolutionError("worker_not_active", `Worker ${JSON.stringify(workerId)} has status ${JSON.stringify(status)} (need active)`, { worker: workerId, status });
|
|
224
|
+
}
|
|
225
|
+
const relativeDir = entry.path.replace(/\/+$/, "");
|
|
226
|
+
const workerDir = path.join(hqRoot, relativeDir);
|
|
227
|
+
const workerYaml = loadWorkerYaml(hqRoot, relativeDir, deps);
|
|
228
|
+
const resolvedContextBase = resolveContextBasePaths(hqRoot, workerDir, workerYaml.context?.base, deps);
|
|
229
|
+
const skillFiles = resolveSkillFiles(workerDir, workerYaml.skills, deps);
|
|
230
|
+
return {
|
|
231
|
+
id: entry.id,
|
|
232
|
+
path: entry.path,
|
|
233
|
+
type: entry.type,
|
|
234
|
+
status,
|
|
235
|
+
visibility: entry.visibility,
|
|
236
|
+
description: entry.description,
|
|
237
|
+
tenancy,
|
|
238
|
+
workerYaml,
|
|
239
|
+
workerDir,
|
|
240
|
+
resolvedContextBase,
|
|
241
|
+
skillFiles,
|
|
242
|
+
defaultTimeout: workerYaml.execution?.max_runtime,
|
|
243
|
+
approvalRequired: workerYaml.verification?.approval_required === true,
|
|
244
|
+
humanCheckpoints: humanCheckpointsOf(workerYaml),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-backed lane / question record store (spec §10, §10.1).
|
|
3
|
+
*
|
|
4
|
+
* Root: workspace/lanes/ under the HQ root. One stable path per record;
|
|
5
|
+
* `status` is the pending/answered discriminator — files are never moved
|
|
6
|
+
* between directories. Every mutation is temp-file-plus-rename and a
|
|
7
|
+
* compare-and-set on `version`. Histories are capped at 50 at the append site.
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from "node:fs";
|
|
10
|
+
import { type AttributionHistoryEntry, type LaneRecord, type QuestionRecord, type RoutingHistoryEntry, type SeniorHistoryEntry } from "./types.js";
|
|
11
|
+
export declare const LANES_STORE_REL: string;
|
|
12
|
+
export declare const LANE_RECORDS_DIR = "lanes";
|
|
13
|
+
export declare const QUESTION_RECORDS_DIR = "questions";
|
|
14
|
+
export declare const PENDING_INDEX_DIR: string;
|
|
15
|
+
export interface StoreDeps {
|
|
16
|
+
now: () => Date;
|
|
17
|
+
hostname: () => string;
|
|
18
|
+
mkdirSync: typeof fs.mkdirSync;
|
|
19
|
+
readFileSync: typeof fs.readFileSync;
|
|
20
|
+
writeFileSync: typeof fs.writeFileSync;
|
|
21
|
+
renameSync: typeof fs.renameSync;
|
|
22
|
+
unlinkSync: typeof fs.unlinkSync;
|
|
23
|
+
existsSync: typeof fs.existsSync;
|
|
24
|
+
readdirSync: typeof fs.readdirSync;
|
|
25
|
+
openSync: typeof fs.openSync;
|
|
26
|
+
closeSync: typeof fs.closeSync;
|
|
27
|
+
fsyncSync: typeof fs.fsyncSync;
|
|
28
|
+
chmodSync: typeof fs.chmodSync;
|
|
29
|
+
}
|
|
30
|
+
export declare function resolveStoreHqRoot(hqRoot?: string): string;
|
|
31
|
+
export declare function lanesStoreRoot(hqRoot?: string): string;
|
|
32
|
+
export declare function laneRecordPath(hqRoot: string, laneId: string): string;
|
|
33
|
+
export declare function questionRecordPath(hqRoot: string, questionId: string): string;
|
|
34
|
+
export declare function pendingMarkerPath(hqRoot: string, questionId: string): string;
|
|
35
|
+
export declare function sanitizeHostComponent(hostname: string): string;
|
|
36
|
+
/** Collision-safe across machines: ULID plus a host component. */
|
|
37
|
+
export declare function mintLaneId(deps?: Pick<StoreDeps, "now" | "hostname">): string;
|
|
38
|
+
export declare function mintQuestionId(deps?: Pick<StoreDeps, "now" | "hostname">): string;
|
|
39
|
+
export declare function appendCappedHistory<T>(history: readonly T[], entry: T, cap?: number): T[];
|
|
40
|
+
export declare function parseLaneRecord(raw: unknown, filePath?: string): LaneRecord;
|
|
41
|
+
export declare function parseQuestionRecord(raw: unknown, filePath?: string): QuestionRecord;
|
|
42
|
+
/**
|
|
43
|
+
* HQ Sync sibling-twin grammar from `@indigoai-us/hq-cloud` `buildConflictPath`:
|
|
44
|
+
* `<file>.json.conflict-<ISO-with-dashes>-<machine>.json`
|
|
45
|
+
* (legacy twins; current engines park losers under `.hq/conflict-backups/`,
|
|
46
|
+
* but merge-on-read still has to recover the ones already on disk).
|
|
47
|
+
*/
|
|
48
|
+
export declare function isConflictSidecarName(recordBasename: string, name: string): boolean;
|
|
49
|
+
export declare function mergeLaneRecords(live: LaneRecord, sidecars: readonly LaneRecord[]): LaneRecord;
|
|
50
|
+
export declare function mergeQuestionRecords(live: QuestionRecord, sidecars: readonly QuestionRecord[]): QuestionRecord;
|
|
51
|
+
export declare function tryReadLane(hqRoot: string, laneId: string, deps?: StoreDeps): LaneRecord | null;
|
|
52
|
+
export declare function readLane(hqRoot: string, laneId: string, deps?: StoreDeps): LaneRecord;
|
|
53
|
+
export declare function tryReadQuestion(hqRoot: string, questionId: string, deps?: StoreDeps): QuestionRecord | null;
|
|
54
|
+
export declare function readQuestion(hqRoot: string, questionId: string, deps?: StoreDeps): QuestionRecord;
|
|
55
|
+
export declare function createLane(hqRoot: string, init: Omit<LaneRecord, "schema_version" | "lane_id" | "version" | "created_at" | "updated_at"> & Partial<Pick<LaneRecord, "schema_version" | "lane_id" | "version" | "created_at" | "updated_at">>, deps?: StoreDeps): LaneRecord;
|
|
56
|
+
export declare function updateLane(hqRoot: string, laneId: string, expectedVersion: number, updater: (current: LaneRecord) => LaneRecord, deps?: StoreDeps): LaneRecord;
|
|
57
|
+
export declare function appendLaneSeniorHistory(hqRoot: string, laneId: string, expectedVersion: number, entry: SeniorHistoryEntry, deps?: StoreDeps): LaneRecord;
|
|
58
|
+
export declare function appendLaneAttribution(hqRoot: string, laneId: string, expectedVersion: number, entry: AttributionHistoryEntry, deps?: StoreDeps): LaneRecord;
|
|
59
|
+
export declare function createQuestion(hqRoot: string, init: Omit<QuestionRecord, "schema_version" | "question_id" | "version"> & Partial<Pick<QuestionRecord, "schema_version" | "question_id" | "version">>, deps?: StoreDeps): QuestionRecord;
|
|
60
|
+
export declare function updateQuestion(hqRoot: string, questionId: string, expectedVersion: number, updater: (current: QuestionRecord) => QuestionRecord, deps?: StoreDeps): QuestionRecord;
|
|
61
|
+
export declare function appendQuestionRouting(hqRoot: string, questionId: string, expectedVersion: number, entry: RoutingHistoryEntry, deps?: StoreDeps): QuestionRecord;
|
|
62
|
+
export declare function listLaneIds(hqRoot: string, deps?: StoreDeps): string[];
|
|
63
|
+
export declare function listQuestionIds(hqRoot: string, deps?: StoreDeps): string[];
|
|
64
|
+
//# sourceMappingURL=store.d.ts.map
|