@ted-galago/wave-cli 0.1.20 → 0.1.22

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/README.md CHANGED
@@ -133,6 +133,8 @@ wave osmd wiki read log.md
133
133
  wave osmd wiki init
134
134
  wave osmd wiki create index.md --content "# Agent Wiki"
135
135
  wave osmd wiki update "Agent Wiki/index.md" --file ./agent-wiki-index.md
136
+ wave osmd wiki create "concepts/customer-health.md" --content "# Customer Health"
137
+ wave osmd wiki update "decisions/use-agent-wiki.md" --content "# Use Agent Wiki"
136
138
  wave osmd wiki append-log --content "Observed durable event"
137
139
  wave find "Ted"
138
140
  wave open "Ted Martinez"
@@ -345,10 +347,11 @@ Path and permission rules:
345
347
  - `.agent` first slice supports only `notes.md`.
346
348
  - `osmd agent init <parent-ref>` is idempotent for default `.agent` files.
347
349
  - Agent Wiki index paths may be `index.md` or `Agent Wiki/index.md`; the CLI normalizes these to `Agent Wiki/index.md`.
350
+ - Controlled Agent Wiki page paths may be `concepts/<name>.md`, `playbooks/<name>.md`, `decisions/<name>.md`, `org-summaries/<name>.md`, or the same forms prefixed with `Agent Wiki/`.
348
351
  - Agent Wiki log paths may be `log.md` or `Agent Wiki/log.md`; the CLI normalizes these to `Agent Wiki/log.md`.
349
- - Agent Wiki first-slice agents should use `index.md` for broad writable wiki memory and `log.md` for append-only event memory.
352
+ - Agent Wiki agents should use `index.md` for broad writable wiki memory, controlled folders for durable typed pages, and `log.md` for append-only event memory.
350
353
  - Agent Wiki `init` initializes backend-owned system files, including append-only `log.md`, through `initAgentMarkdown` at path `Agent Wiki`.
351
- - Agent Wiki `create` and `update` support only `index.md`; `log.md` cannot be overwritten.
354
+ - Agent Wiki `create` and `update` support `index.md`, `concepts/*.md`, `playbooks/*.md`, `decisions/*.md`, and `org-summaries/*.md`; `log.md` cannot be created or overwritten by these commands.
352
355
  - Agent Wiki `append-log` supports only `log.md` and calls status first before appending.
353
356
  - The CLI uses backend GraphQL metadata directly and does not derive S3 paths, SHA paths, or backend storage conventions.
354
357
  - Atlas should inspect `path`, `source`, `access`, `owner`, `parentRef`, `exists`, `canCreate`, `canUpdate`, `canAppend`, `wikiRole`, `formatVersion`, `suggestedAction`, and `errorCode`.
@@ -361,6 +364,7 @@ Path and permission rules:
361
364
  - `append-log` fails unless status reports `exists: true`, `access: "append_only"`, and `canAppend: true`.
362
365
  - When `Agent Wiki/log.md` is missing and status reports `suggestedAction: "init"`, `append-log` returns a JSON error telling Atlas to run `wave osmd wiki init`.
363
366
  - Missing `Agent Wiki/index.md` is create-ready when status returns `exists: false`, `canCreate: true`, `canUpdate: false`, and `suggestedAction: "create"`.
367
+ - Missing controlled pages are create-ready when backend status returns `exists: false`, `canCreate: true`, `canUpdate: false`, and `suggestedAction: "create"`.
364
368
 
365
369
  Atlas Agent Wiki log flow:
366
370
 
@@ -382,6 +386,22 @@ wave osmd wiki read index.md
382
386
  wave osmd wiki update index.md --content "# Agent Wiki\n\n## Entity Notes\n\n- directory/Wave Tools Team -> .agent/notes.md"
383
387
  ```
384
388
 
389
+ Create or update controlled Agent Wiki pages. Atlas should call `status` first, then choose `create` or `update` from backend `canCreate`, `canUpdate`, and `suggestedAction`:
390
+
391
+ ```bash
392
+ wave osmd wiki status concepts/customer-health.md
393
+ wave osmd wiki create concepts/customer-health.md --content "# Customer Health\n\nDurable definition and usage notes."
394
+
395
+ wave osmd wiki status decisions/use-agent-wiki.md
396
+ wave osmd wiki update decisions/use-agent-wiki.md --content "# Use Agent Wiki\n\nDecision: store broad agent memory in controlled Agent Wiki pages."
397
+
398
+ wave osmd wiki status playbooks/customer-review.md
399
+ wave osmd wiki create playbooks/customer-review.md --content "# Customer Review\n\n1. Read the customer summary.\n2. Review recent notes.\n3. Append the maintenance log."
400
+
401
+ wave osmd wiki status org-summaries/galago.md
402
+ wave osmd wiki update org-summaries/galago.md --content "# Galago\n\nCurrent durable organization summary."
403
+ ```
404
+
385
405
  Append a parseable log entry. Use a stable fenced block so Atlas or backend jobs can parse the chronology later:
386
406
 
387
407
  ````bash
@@ -529,7 +549,7 @@ Use `wave osmd search "<query>" --scope combined` when Atlas needs merged canoni
529
549
 
530
550
  - `--scope os-only` searches canonical OSMD only.
531
551
  - `--scope agent-overlay-only` searches entity-specific `.agent` files.
532
- - `--scope agent-wiki-only` searches broad Agent Wiki files, including `index.md` and append-only `log.md`.
552
+ - `--scope agent-wiki-only` searches broad Agent Wiki files, including `index.md`, controlled pages, and append-only `log.md`.
533
553
  - `--scope combined` searches the backend merged OSMD surface.
534
554
 
535
555
  Ambiguity contract:
package/dist/index.cjs CHANGED
@@ -6147,6 +6147,15 @@ var positiveInt = import_zod16.z.coerce.number().int().min(1);
6147
6147
  var supportedAgentOverlayFile = "notes.md";
6148
6148
  var supportedWikiIndexFile = "index.md";
6149
6149
  var supportedWikiLogFile = "log.md";
6150
+ var supportedWikiControlledFolders = [
6151
+ "concepts",
6152
+ "playbooks",
6153
+ "decisions",
6154
+ "org-summaries"
6155
+ ];
6156
+ var supportedWikiControlledPatterns = supportedWikiControlledFolders.map(
6157
+ (folder) => `${folder}/*.md`
6158
+ );
6150
6159
  var agentOverlayFileSchema = import_zod16.z.enum([supportedAgentOverlayFile]);
6151
6160
  var osmdSearchScopeSchema = import_zod16.z.enum([
6152
6161
  "os-only",
@@ -6540,41 +6549,113 @@ function normalizeAgentFilePath(file) {
6540
6549
  }
6541
6550
  return `.agent/${parsed.data}`;
6542
6551
  }
6543
- function supportedWikiFilesMessage(files) {
6544
- return `Agent Wiki file commands support only ${files.join(" and ")} in this CLI slice.`;
6552
+ function formatHumanList(items) {
6553
+ if (items.length <= 1) {
6554
+ return items[0] ?? "";
6555
+ }
6556
+ if (items.length === 2) {
6557
+ return `${items[0]} and ${items[1]}`;
6558
+ }
6559
+ return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
6560
+ }
6561
+ function supportedWikiPathsMessage(options = {}) {
6562
+ const allowed = allowedWikiPathLabels(options);
6563
+ return `Agent Wiki file commands support only ${formatHumanList(allowed)} in this CLI slice.`;
6564
+ }
6565
+ function allowedWikiPathLabels(options = {}) {
6566
+ const allowIndex = options.allowIndex ?? true;
6567
+ const allowLog = options.allowLog ?? true;
6568
+ const allowControlledPages = options.allowControlledPages ?? true;
6569
+ const labels = [];
6570
+ if (options.allowRoot) {
6571
+ labels.push("Agent Wiki");
6572
+ }
6573
+ if (allowIndex) {
6574
+ labels.push(supportedWikiIndexFile);
6575
+ }
6576
+ if (allowLog) {
6577
+ labels.push(supportedWikiLogFile);
6578
+ }
6579
+ if (allowControlledPages) {
6580
+ labels.push(...supportedWikiControlledPatterns);
6581
+ }
6582
+ if (options.allowControlledFolders) {
6583
+ labels.push(...supportedWikiControlledFolders);
6584
+ }
6585
+ return labels;
6586
+ }
6587
+ function rejectUnsupportedWikiPath(options) {
6588
+ throw new CliError({
6589
+ message: supportedWikiPathsMessage(options),
6590
+ kind: "invalid_args",
6591
+ status: 400,
6592
+ exitCode: EXIT_CODES.invalidArgs,
6593
+ details: { supportedPaths: allowedWikiPathLabels(options) }
6594
+ });
6595
+ }
6596
+ function isControlledWikiFolder(value) {
6597
+ return supportedWikiControlledFolders.includes(
6598
+ value
6599
+ );
6600
+ }
6601
+ function isMarkdownLeafFileName(value) {
6602
+ return value.endsWith(".md") && value.length > ".md".length;
6545
6603
  }
6546
6604
  function normalizeWikiPath(path, options = {}) {
6547
- const supportedFiles = options.supportedFiles ?? [supportedWikiIndexFile, supportedWikiLogFile];
6605
+ const normalizedOptions = {
6606
+ allowIndex: options.allowIndex ?? true,
6607
+ allowLog: options.allowLog ?? true,
6608
+ allowControlledPages: options.allowControlledPages ?? true,
6609
+ allowRoot: options.allowRoot ?? false,
6610
+ allowControlledFolders: options.allowControlledFolders ?? false
6611
+ };
6548
6612
  const trimmed = nonEmptyString3.parse(path).trim().replace(/^\/+/, "");
6549
6613
  if (trimmed.localeCompare("Agent Wiki", void 0, { sensitivity: "accent" }) === 0) {
6550
- if (options.allowRoot) {
6614
+ if (normalizedOptions.allowRoot) {
6551
6615
  return "Agent Wiki";
6552
6616
  }
6553
- throw new CliError({
6554
- message: supportedWikiFilesMessage(supportedFiles),
6555
- kind: "invalid_args",
6556
- status: 400,
6557
- exitCode: EXIT_CODES.invalidArgs,
6558
- details: { supportedFiles }
6559
- });
6617
+ return rejectUnsupportedWikiPath(normalizedOptions);
6560
6618
  }
6561
6619
  const fileName = trimmed.toLowerCase().startsWith("agent wiki/") ? trimmed.slice("Agent Wiki/".length) : trimmed;
6562
- if (!supportedFiles.includes(fileName)) {
6563
- throw new CliError({
6564
- message: supportedWikiFilesMessage(supportedFiles),
6565
- kind: "invalid_args",
6566
- status: 400,
6567
- exitCode: EXIT_CODES.invalidArgs,
6568
- details: { supportedFiles }
6569
- });
6620
+ if (fileName.includes("\\") || fileName.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) {
6621
+ return rejectUnsupportedWikiPath(normalizedOptions);
6622
+ }
6623
+ if (fileName === supportedWikiIndexFile) {
6624
+ if (normalizedOptions.allowIndex) {
6625
+ return `Agent Wiki/${fileName}`;
6626
+ }
6627
+ return rejectUnsupportedWikiPath(normalizedOptions);
6628
+ }
6629
+ if (fileName === supportedWikiLogFile) {
6630
+ if (normalizedOptions.allowLog) {
6631
+ return `Agent Wiki/${fileName}`;
6632
+ }
6633
+ return rejectUnsupportedWikiPath(normalizedOptions);
6570
6634
  }
6571
- return `Agent Wiki/${fileName}`;
6635
+ if (isControlledWikiFolder(fileName)) {
6636
+ if (normalizedOptions.allowControlledFolders) {
6637
+ return `Agent Wiki/${fileName}`;
6638
+ }
6639
+ return rejectUnsupportedWikiPath(normalizedOptions);
6640
+ }
6641
+ const parts = fileName.split("/");
6642
+ if (parts.length === 2 && isControlledWikiFolder(parts[0] ?? "") && isMarkdownLeafFileName(parts[1] ?? "")) {
6643
+ if (normalizedOptions.allowControlledPages) {
6644
+ return `Agent Wiki/${fileName}`;
6645
+ }
6646
+ return rejectUnsupportedWikiPath(normalizedOptions);
6647
+ }
6648
+ return rejectUnsupportedWikiPath(normalizedOptions);
6572
6649
  }
6573
- function normalizeWikiIndexPath(path) {
6574
- return normalizeWikiPath(path, { supportedFiles: [supportedWikiIndexFile] });
6650
+ function normalizeWikiWritablePath(path) {
6651
+ return normalizeWikiPath(path, { allowLog: false });
6575
6652
  }
6576
6653
  function normalizeWikiLogPath(path = supportedWikiLogFile) {
6577
- return normalizeWikiPath(path, { supportedFiles: [supportedWikiLogFile] });
6654
+ return normalizeWikiPath(path, {
6655
+ allowIndex: false,
6656
+ allowLog: true,
6657
+ allowControlledPages: false
6658
+ });
6578
6659
  }
6579
6660
  function parseOptionalParentRef(value) {
6580
6661
  return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
@@ -7049,7 +7130,7 @@ function registerOsmdCommands(program) {
7049
7130
  });
7050
7131
  }
7051
7132
  );
7052
- const wiki = osmd.command("wiki").description("Agent Wiki files writable by agents; supports index.md and append-only log.md");
7133
+ const wiki = osmd.command("wiki").description("Agent Wiki files writable by agents; supports index.md, controlled pages, and append-only log.md");
7053
7134
  wiki.command("init").description("Idempotently initialize Agent Wiki system files").action(async function() {
7054
7135
  const { organizationId, runtimeOptions } = await resolveOsmdContext(this);
7055
7136
  return runAgentMarkdownMutation({
@@ -7063,7 +7144,7 @@ function registerOsmdCommands(program) {
7063
7144
  }
7064
7145
  });
7065
7146
  });
7066
- wiki.command("status <path>").description("Show Agent Wiki index.md or log.md metadata without creating files").action(async (path, _opts, cmd) => {
7147
+ wiki.command("status <path>").description("Show Agent Wiki index.md, log.md, or controlled page metadata without creating files").action(async (path, _opts, cmd) => {
7067
7148
  const { organizationId, runtimeOptions } = await resolveOsmdContext(cmd);
7068
7149
  const result = await requestAgentMarkdownStatus({
7069
7150
  command: "osmd.wiki.status",
@@ -7080,7 +7161,7 @@ function registerOsmdCommands(program) {
7080
7161
  })
7081
7162
  });
7082
7163
  });
7083
- wiki.command("read <path>").description("Read Agent Wiki index.md or log.md without creating files").action(async (path, _opts, cmd) => {
7164
+ wiki.command("read <path>").description("Read Agent Wiki index.md, log.md, or controlled pages without creating files").action(async (path, _opts, cmd) => {
7084
7165
  const { organizationId, runtimeOptions } = await resolveOsmdContext(cmd);
7085
7166
  const result = await requestAgentMarkdownFile({
7086
7167
  command: "osmd.wiki.read",
@@ -7103,7 +7184,7 @@ function registerOsmdCommands(program) {
7103
7184
  command: "osmd.wiki.children",
7104
7185
  runtimeOptions,
7105
7186
  organizationId,
7106
- path: typeof path === "string" && path.trim() !== "" ? normalizeWikiPath(path, { allowRoot: true }) : "Agent Wiki",
7187
+ path: typeof path === "string" && path.trim() !== "" ? normalizeWikiPath(path, { allowRoot: true, allowControlledFolders: true }) : "Agent Wiki",
7107
7188
  source: "agent_wiki"
7108
7189
  });
7109
7190
  return printEnvelopeAndExit({
@@ -7116,7 +7197,7 @@ function registerOsmdCommands(program) {
7116
7197
  });
7117
7198
  });
7118
7199
  addContentOptions(
7119
- wiki.command("create <path>").description("Create Agent Wiki index.md after status/permission preflight")
7200
+ wiki.command("create <path>").description("Create Agent Wiki index.md or controlled pages after status/permission preflight")
7120
7201
  ).action(async (path, opts, cmd) => {
7121
7202
  const globals = cmd.optsWithGlobals();
7122
7203
  const content = await resolveContentInput(opts, globals);
@@ -7126,12 +7207,12 @@ function registerOsmdCommands(program) {
7126
7207
  action: "create",
7127
7208
  runtimeOptions,
7128
7209
  organizationId,
7129
- path: normalizeWikiIndexPath(path),
7210
+ path: normalizeWikiWritablePath(path),
7130
7211
  content
7131
7212
  });
7132
7213
  });
7133
7214
  addContentOptions(
7134
- wiki.command("update <path>").description("Update Agent Wiki index.md after status/permission preflight")
7215
+ wiki.command("update <path>").description("Update Agent Wiki index.md or controlled pages after status/permission preflight")
7135
7216
  ).action(async (path, opts, cmd) => {
7136
7217
  const globals = cmd.optsWithGlobals();
7137
7218
  const content = await resolveContentInput(opts, globals);
@@ -7141,7 +7222,7 @@ function registerOsmdCommands(program) {
7141
7222
  action: "update",
7142
7223
  runtimeOptions,
7143
7224
  organizationId,
7144
- path: normalizeWikiIndexPath(path),
7225
+ path: normalizeWikiWritablePath(path),
7145
7226
  content
7146
7227
  });
7147
7228
  });
package/dist/index.js CHANGED
@@ -6146,6 +6146,15 @@ var positiveInt = z16.coerce.number().int().min(1);
6146
6146
  var supportedAgentOverlayFile = "notes.md";
6147
6147
  var supportedWikiIndexFile = "index.md";
6148
6148
  var supportedWikiLogFile = "log.md";
6149
+ var supportedWikiControlledFolders = [
6150
+ "concepts",
6151
+ "playbooks",
6152
+ "decisions",
6153
+ "org-summaries"
6154
+ ];
6155
+ var supportedWikiControlledPatterns = supportedWikiControlledFolders.map(
6156
+ (folder) => `${folder}/*.md`
6157
+ );
6149
6158
  var agentOverlayFileSchema = z16.enum([supportedAgentOverlayFile]);
6150
6159
  var osmdSearchScopeSchema = z16.enum([
6151
6160
  "os-only",
@@ -6539,41 +6548,113 @@ function normalizeAgentFilePath(file) {
6539
6548
  }
6540
6549
  return `.agent/${parsed.data}`;
6541
6550
  }
6542
- function supportedWikiFilesMessage(files) {
6543
- return `Agent Wiki file commands support only ${files.join(" and ")} in this CLI slice.`;
6551
+ function formatHumanList(items) {
6552
+ if (items.length <= 1) {
6553
+ return items[0] ?? "";
6554
+ }
6555
+ if (items.length === 2) {
6556
+ return `${items[0]} and ${items[1]}`;
6557
+ }
6558
+ return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
6559
+ }
6560
+ function supportedWikiPathsMessage(options = {}) {
6561
+ const allowed = allowedWikiPathLabels(options);
6562
+ return `Agent Wiki file commands support only ${formatHumanList(allowed)} in this CLI slice.`;
6563
+ }
6564
+ function allowedWikiPathLabels(options = {}) {
6565
+ const allowIndex = options.allowIndex ?? true;
6566
+ const allowLog = options.allowLog ?? true;
6567
+ const allowControlledPages = options.allowControlledPages ?? true;
6568
+ const labels = [];
6569
+ if (options.allowRoot) {
6570
+ labels.push("Agent Wiki");
6571
+ }
6572
+ if (allowIndex) {
6573
+ labels.push(supportedWikiIndexFile);
6574
+ }
6575
+ if (allowLog) {
6576
+ labels.push(supportedWikiLogFile);
6577
+ }
6578
+ if (allowControlledPages) {
6579
+ labels.push(...supportedWikiControlledPatterns);
6580
+ }
6581
+ if (options.allowControlledFolders) {
6582
+ labels.push(...supportedWikiControlledFolders);
6583
+ }
6584
+ return labels;
6585
+ }
6586
+ function rejectUnsupportedWikiPath(options) {
6587
+ throw new CliError({
6588
+ message: supportedWikiPathsMessage(options),
6589
+ kind: "invalid_args",
6590
+ status: 400,
6591
+ exitCode: EXIT_CODES.invalidArgs,
6592
+ details: { supportedPaths: allowedWikiPathLabels(options) }
6593
+ });
6594
+ }
6595
+ function isControlledWikiFolder(value) {
6596
+ return supportedWikiControlledFolders.includes(
6597
+ value
6598
+ );
6599
+ }
6600
+ function isMarkdownLeafFileName(value) {
6601
+ return value.endsWith(".md") && value.length > ".md".length;
6544
6602
  }
6545
6603
  function normalizeWikiPath(path, options = {}) {
6546
- const supportedFiles = options.supportedFiles ?? [supportedWikiIndexFile, supportedWikiLogFile];
6604
+ const normalizedOptions = {
6605
+ allowIndex: options.allowIndex ?? true,
6606
+ allowLog: options.allowLog ?? true,
6607
+ allowControlledPages: options.allowControlledPages ?? true,
6608
+ allowRoot: options.allowRoot ?? false,
6609
+ allowControlledFolders: options.allowControlledFolders ?? false
6610
+ };
6547
6611
  const trimmed = nonEmptyString3.parse(path).trim().replace(/^\/+/, "");
6548
6612
  if (trimmed.localeCompare("Agent Wiki", void 0, { sensitivity: "accent" }) === 0) {
6549
- if (options.allowRoot) {
6613
+ if (normalizedOptions.allowRoot) {
6550
6614
  return "Agent Wiki";
6551
6615
  }
6552
- throw new CliError({
6553
- message: supportedWikiFilesMessage(supportedFiles),
6554
- kind: "invalid_args",
6555
- status: 400,
6556
- exitCode: EXIT_CODES.invalidArgs,
6557
- details: { supportedFiles }
6558
- });
6616
+ return rejectUnsupportedWikiPath(normalizedOptions);
6559
6617
  }
6560
6618
  const fileName = trimmed.toLowerCase().startsWith("agent wiki/") ? trimmed.slice("Agent Wiki/".length) : trimmed;
6561
- if (!supportedFiles.includes(fileName)) {
6562
- throw new CliError({
6563
- message: supportedWikiFilesMessage(supportedFiles),
6564
- kind: "invalid_args",
6565
- status: 400,
6566
- exitCode: EXIT_CODES.invalidArgs,
6567
- details: { supportedFiles }
6568
- });
6619
+ if (fileName.includes("\\") || fileName.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) {
6620
+ return rejectUnsupportedWikiPath(normalizedOptions);
6621
+ }
6622
+ if (fileName === supportedWikiIndexFile) {
6623
+ if (normalizedOptions.allowIndex) {
6624
+ return `Agent Wiki/${fileName}`;
6625
+ }
6626
+ return rejectUnsupportedWikiPath(normalizedOptions);
6627
+ }
6628
+ if (fileName === supportedWikiLogFile) {
6629
+ if (normalizedOptions.allowLog) {
6630
+ return `Agent Wiki/${fileName}`;
6631
+ }
6632
+ return rejectUnsupportedWikiPath(normalizedOptions);
6569
6633
  }
6570
- return `Agent Wiki/${fileName}`;
6634
+ if (isControlledWikiFolder(fileName)) {
6635
+ if (normalizedOptions.allowControlledFolders) {
6636
+ return `Agent Wiki/${fileName}`;
6637
+ }
6638
+ return rejectUnsupportedWikiPath(normalizedOptions);
6639
+ }
6640
+ const parts = fileName.split("/");
6641
+ if (parts.length === 2 && isControlledWikiFolder(parts[0] ?? "") && isMarkdownLeafFileName(parts[1] ?? "")) {
6642
+ if (normalizedOptions.allowControlledPages) {
6643
+ return `Agent Wiki/${fileName}`;
6644
+ }
6645
+ return rejectUnsupportedWikiPath(normalizedOptions);
6646
+ }
6647
+ return rejectUnsupportedWikiPath(normalizedOptions);
6571
6648
  }
6572
- function normalizeWikiIndexPath(path) {
6573
- return normalizeWikiPath(path, { supportedFiles: [supportedWikiIndexFile] });
6649
+ function normalizeWikiWritablePath(path) {
6650
+ return normalizeWikiPath(path, { allowLog: false });
6574
6651
  }
6575
6652
  function normalizeWikiLogPath(path = supportedWikiLogFile) {
6576
- return normalizeWikiPath(path, { supportedFiles: [supportedWikiLogFile] });
6653
+ return normalizeWikiPath(path, {
6654
+ allowIndex: false,
6655
+ allowLog: true,
6656
+ allowControlledPages: false
6657
+ });
6577
6658
  }
6578
6659
  function parseOptionalParentRef(value) {
6579
6660
  return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
@@ -7048,7 +7129,7 @@ function registerOsmdCommands(program) {
7048
7129
  });
7049
7130
  }
7050
7131
  );
7051
- const wiki = osmd.command("wiki").description("Agent Wiki files writable by agents; supports index.md and append-only log.md");
7132
+ const wiki = osmd.command("wiki").description("Agent Wiki files writable by agents; supports index.md, controlled pages, and append-only log.md");
7052
7133
  wiki.command("init").description("Idempotently initialize Agent Wiki system files").action(async function() {
7053
7134
  const { organizationId, runtimeOptions } = await resolveOsmdContext(this);
7054
7135
  return runAgentMarkdownMutation({
@@ -7062,7 +7143,7 @@ function registerOsmdCommands(program) {
7062
7143
  }
7063
7144
  });
7064
7145
  });
7065
- wiki.command("status <path>").description("Show Agent Wiki index.md or log.md metadata without creating files").action(async (path, _opts, cmd) => {
7146
+ wiki.command("status <path>").description("Show Agent Wiki index.md, log.md, or controlled page metadata without creating files").action(async (path, _opts, cmd) => {
7066
7147
  const { organizationId, runtimeOptions } = await resolveOsmdContext(cmd);
7067
7148
  const result = await requestAgentMarkdownStatus({
7068
7149
  command: "osmd.wiki.status",
@@ -7079,7 +7160,7 @@ function registerOsmdCommands(program) {
7079
7160
  })
7080
7161
  });
7081
7162
  });
7082
- wiki.command("read <path>").description("Read Agent Wiki index.md or log.md without creating files").action(async (path, _opts, cmd) => {
7163
+ wiki.command("read <path>").description("Read Agent Wiki index.md, log.md, or controlled pages without creating files").action(async (path, _opts, cmd) => {
7083
7164
  const { organizationId, runtimeOptions } = await resolveOsmdContext(cmd);
7084
7165
  const result = await requestAgentMarkdownFile({
7085
7166
  command: "osmd.wiki.read",
@@ -7102,7 +7183,7 @@ function registerOsmdCommands(program) {
7102
7183
  command: "osmd.wiki.children",
7103
7184
  runtimeOptions,
7104
7185
  organizationId,
7105
- path: typeof path === "string" && path.trim() !== "" ? normalizeWikiPath(path, { allowRoot: true }) : "Agent Wiki",
7186
+ path: typeof path === "string" && path.trim() !== "" ? normalizeWikiPath(path, { allowRoot: true, allowControlledFolders: true }) : "Agent Wiki",
7106
7187
  source: "agent_wiki"
7107
7188
  });
7108
7189
  return printEnvelopeAndExit({
@@ -7115,7 +7196,7 @@ function registerOsmdCommands(program) {
7115
7196
  });
7116
7197
  });
7117
7198
  addContentOptions(
7118
- wiki.command("create <path>").description("Create Agent Wiki index.md after status/permission preflight")
7199
+ wiki.command("create <path>").description("Create Agent Wiki index.md or controlled pages after status/permission preflight")
7119
7200
  ).action(async (path, opts, cmd) => {
7120
7201
  const globals = cmd.optsWithGlobals();
7121
7202
  const content = await resolveContentInput(opts, globals);
@@ -7125,12 +7206,12 @@ function registerOsmdCommands(program) {
7125
7206
  action: "create",
7126
7207
  runtimeOptions,
7127
7208
  organizationId,
7128
- path: normalizeWikiIndexPath(path),
7209
+ path: normalizeWikiWritablePath(path),
7129
7210
  content
7130
7211
  });
7131
7212
  });
7132
7213
  addContentOptions(
7133
- wiki.command("update <path>").description("Update Agent Wiki index.md after status/permission preflight")
7214
+ wiki.command("update <path>").description("Update Agent Wiki index.md or controlled pages after status/permission preflight")
7134
7215
  ).action(async (path, opts, cmd) => {
7135
7216
  const globals = cmd.optsWithGlobals();
7136
7217
  const content = await resolveContentInput(opts, globals);
@@ -7140,7 +7221,7 @@ function registerOsmdCommands(program) {
7140
7221
  action: "update",
7141
7222
  runtimeOptions,
7142
7223
  organizationId,
7143
- path: normalizeWikiIndexPath(path),
7224
+ path: normalizeWikiWritablePath(path),
7144
7225
  content
7145
7226
  });
7146
7227
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ted-galago/wave-cli",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "wave": "dist/index.js"
@@ -1330,6 +1330,124 @@ function runOsmdLiveVerification() {
1330
1330
  );
1331
1331
  }
1332
1332
 
1333
+ const controlledWikiSearchTerm = `CLI live controlled wiki ${verifyStamp}`;
1334
+ const controlledWikiPages = [
1335
+ {
1336
+ path: `concepts/cli-live-concept-${verifyStamp}.md`,
1337
+ fullPath: `Agent Wiki/concepts/cli-live-concept-${verifyStamp}.md`,
1338
+ wikiRole: "concept",
1339
+ title: "concept"
1340
+ },
1341
+ {
1342
+ path: `playbooks/cli-live-playbook-${verifyStamp}.md`,
1343
+ fullPath: `Agent Wiki/playbooks/cli-live-playbook-${verifyStamp}.md`,
1344
+ wikiRole: "playbook",
1345
+ title: "playbook"
1346
+ },
1347
+ {
1348
+ path: `decisions/cli-live-decision-${verifyStamp}.md`,
1349
+ fullPath: `Agent Wiki/decisions/cli-live-decision-${verifyStamp}.md`,
1350
+ wikiRole: "decision",
1351
+ title: "decision"
1352
+ },
1353
+ {
1354
+ path: `org-summaries/cli-live-org-summary-${verifyStamp}.md`,
1355
+ fullPath: `Agent Wiki/org-summaries/cli-live-org-summary-${verifyStamp}.md`,
1356
+ wikiRole: "org_summary",
1357
+ title: "org_summary"
1358
+ }
1359
+ ];
1360
+
1361
+ for (const page of controlledWikiPages) {
1362
+ const controlledStatus = runWave(["osmd", "wiki", "status", page.path], env);
1363
+ results.push(controlledStatus);
1364
+ osmdVerificationFailures += recordExactCheck({
1365
+ lines: osmdVerificationLines,
1366
+ label: `osmd.wiki status controlled ${page.title}`,
1367
+ result: controlledStatus,
1368
+ ok:
1369
+ controlledStatus.parsed?.ok === true &&
1370
+ controlledStatus.parsed?.data?.path === page.fullPath &&
1371
+ controlledStatus.parsed?.data?.source === "agent_wiki" &&
1372
+ controlledStatus.parsed?.data?.access === "read_write" &&
1373
+ controlledStatus.parsed?.data?.wikiRole === page.wikiRole &&
1374
+ controlledStatus.parsed?.data?.formatVersion === "agent_markdown_v1" &&
1375
+ controlledStatus.parsed?.data?.exists === false &&
1376
+ controlledStatus.parsed?.data?.canCreate === true &&
1377
+ controlledStatus.parsed?.data?.canUpdate === false &&
1378
+ controlledStatus.parsed?.data?.canAppend === false &&
1379
+ controlledStatus.parsed?.data?.suggestedAction === "create",
1380
+ reason: "agent_wiki_controlled_status"
1381
+ });
1382
+
1383
+ const createContent = `# CLI Live ${page.title}\n\n${controlledWikiSearchTerm} create ${page.title}`;
1384
+ const controlledCreate = runWave(["osmd", "wiki", "create", page.path, "--content", createContent], env);
1385
+ results.push(controlledCreate);
1386
+ osmdVerificationFailures += recordExactCheck({
1387
+ lines: osmdVerificationLines,
1388
+ label: `osmd.wiki create controlled ${page.title}`,
1389
+ result: controlledCreate,
1390
+ ok:
1391
+ controlledCreate.parsed?.ok === true &&
1392
+ controlledCreate.parsed?.data?.path === page.fullPath &&
1393
+ controlledCreate.parsed?.data?.source === "agent_wiki" &&
1394
+ controlledCreate.parsed?.data?.wikiRole === page.wikiRole &&
1395
+ controlledCreate.parsed?.data?.formatVersion === "agent_markdown_v1" &&
1396
+ controlledCreate.parsed?.data?.exists === true &&
1397
+ controlledCreate.parsed?.data?.canUpdate === true &&
1398
+ controlledCreate.parsed?.data?.canAppend === false &&
1399
+ controlledCreate.parsed?.data?.content === createContent,
1400
+ reason: "agent_wiki_controlled_create"
1401
+ });
1402
+
1403
+ const updateContent = `# CLI Live ${page.title}\n\n${controlledWikiSearchTerm} update ${page.title}`;
1404
+ const controlledUpdate = runWave(["osmd", "wiki", "update", page.path, "--content", updateContent], env);
1405
+ results.push(controlledUpdate);
1406
+ osmdVerificationFailures += recordExactCheck({
1407
+ lines: osmdVerificationLines,
1408
+ label: `osmd.wiki update controlled ${page.title}`,
1409
+ result: controlledUpdate,
1410
+ ok:
1411
+ controlledUpdate.parsed?.ok === true &&
1412
+ controlledUpdate.parsed?.data?.path === page.fullPath &&
1413
+ controlledUpdate.parsed?.data?.source === "agent_wiki" &&
1414
+ controlledUpdate.parsed?.data?.wikiRole === page.wikiRole &&
1415
+ controlledUpdate.parsed?.data?.formatVersion === "agent_markdown_v1" &&
1416
+ controlledUpdate.parsed?.data?.exists === true &&
1417
+ controlledUpdate.parsed?.data?.canUpdate === true &&
1418
+ controlledUpdate.parsed?.data?.canAppend === false &&
1419
+ controlledUpdate.parsed?.data?.content === updateContent,
1420
+ reason: "agent_wiki_controlled_update"
1421
+ });
1422
+ }
1423
+
1424
+ const logUpdateRejected = runWave(["osmd", "wiki", "update", "log.md", "--content", "verify forbidden"], env);
1425
+ osmdVerificationFailures += recordExactCheck({
1426
+ lines: osmdVerificationLines,
1427
+ label: "osmd.wiki update rejects log locally",
1428
+ result: logUpdateRejected,
1429
+ ok:
1430
+ logUpdateRejected.parsed?.ok === false &&
1431
+ logUpdateRejected.parsed?.status === 400 &&
1432
+ logUpdateRejected.parsed?.error?.code === "invalid_args",
1433
+ reason: "agent_wiki_log_update_local_reject"
1434
+ });
1435
+
1436
+ const arbitraryWikiRejected = runWave(
1437
+ ["osmd", "wiki", "create", "random/cli-live-random.md", "--content", "verify forbidden"],
1438
+ env
1439
+ );
1440
+ osmdVerificationFailures += recordExactCheck({
1441
+ lines: osmdVerificationLines,
1442
+ label: "osmd.wiki create rejects arbitrary wiki path locally",
1443
+ result: arbitraryWikiRejected,
1444
+ ok:
1445
+ arbitraryWikiRejected.parsed?.ok === false &&
1446
+ arbitraryWikiRejected.parsed?.status === 400 &&
1447
+ arbitraryWikiRejected.parsed?.error?.code === "invalid_args",
1448
+ reason: "agent_wiki_arbitrary_path_local_reject"
1449
+ });
1450
+
1333
1451
  const parentRef = resolveOsmdAgentParentRef();
1334
1452
  if (!parentRef) {
1335
1453
  osmdVerificationLines.push(
@@ -1452,6 +1570,55 @@ function runOsmdLiveVerification() {
1452
1570
  reason: "osmd_search_agent_wiki"
1453
1571
  });
1454
1572
 
1573
+ const controlledWikiSearch = runWave(
1574
+ ["osmd", "search", controlledWikiSearchTerm, "--scope", "agent-wiki-only", "--limit", "10"],
1575
+ env
1576
+ );
1577
+ results.push(controlledWikiSearch);
1578
+ const controlledWikiSearchCandidates = searchCandidates(controlledWikiSearch);
1579
+ osmdVerificationFailures += recordExactCheck({
1580
+ lines: osmdVerificationLines,
1581
+ label: "osmd.search Agent Wiki controlled pages",
1582
+ result: controlledWikiSearch,
1583
+ ok:
1584
+ controlledWikiSearch.parsed?.ok === true &&
1585
+ controlledWikiSearch.parsed?.data?.scope === "agent_wiki" &&
1586
+ controlledWikiPages.every((page) =>
1587
+ controlledWikiSearchCandidates.some(
1588
+ (candidate) =>
1589
+ hasSearchMetadata(candidate, "agent_wiki") &&
1590
+ candidate.path === page.fullPath &&
1591
+ candidate.wikiRole === page.wikiRole &&
1592
+ candidate.access === "read_write"
1593
+ )
1594
+ ),
1595
+ reason: "osmd_search_agent_wiki_controlled_pages"
1596
+ });
1597
+
1598
+ const controlledCombinedSearch = runWave(
1599
+ ["osmd", "search", controlledWikiSearchTerm, "--scope", "combined", "--limit", "10"],
1600
+ env
1601
+ );
1602
+ results.push(controlledCombinedSearch);
1603
+ const controlledCombinedSearchCandidates = searchCandidates(controlledCombinedSearch);
1604
+ osmdVerificationFailures += recordExactCheck({
1605
+ lines: osmdVerificationLines,
1606
+ label: "osmd.search combined includes controlled Agent Wiki pages",
1607
+ result: controlledCombinedSearch,
1608
+ ok:
1609
+ controlledCombinedSearch.parsed?.ok === true &&
1610
+ controlledCombinedSearch.parsed?.data?.scope === "combined" &&
1611
+ controlledWikiPages.some((page) =>
1612
+ controlledCombinedSearchCandidates.some(
1613
+ (candidate) =>
1614
+ hasSearchMetadata(candidate, "agent_wiki") &&
1615
+ candidate.path === page.fullPath &&
1616
+ candidate.wikiRole === page.wikiRole
1617
+ )
1618
+ ),
1619
+ reason: "osmd_search_combined_controlled_pages"
1620
+ });
1621
+
1455
1622
  const logSearch = runWave(
1456
1623
  ["osmd", "search", `CLI live verify log ${verifyStamp}`, "--scope", "agent-wiki-only", "--limit", "5"],
1457
1624
  env