@rubytech/create-realagent-code 0.1.29 → 0.1.31

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 (115) hide show
  1. package/dist/__tests__/init-logging.test.js +85 -0
  2. package/dist/index.js +21 -10
  3. package/dist/init-logging.js +28 -0
  4. package/package.json +1 -1
  5. package/payload/platform/lib/persistent-components/dist/index.d.ts +11 -12
  6. package/payload/platform/lib/persistent-components/dist/index.d.ts.map +1 -1
  7. package/payload/platform/lib/persistent-components/dist/index.js +11 -12
  8. package/payload/platform/lib/persistent-components/dist/index.js.map +1 -1
  9. package/payload/platform/lib/persistent-components/src/index.ts +11 -12
  10. package/payload/platform/neo4j/schema.cypher +6 -3
  11. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  12. package/payload/platform/plugins/admin/PLUGIN.md +1 -3
  13. package/payload/platform/plugins/admin/hooks/onboarding-skill-drift.sh +57 -54
  14. package/payload/platform/plugins/admin/mcp/dist/index.js +15 -50
  15. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  16. package/payload/platform/plugins/admin/skills/access-manager/references/operations.md +7 -7
  17. package/payload/platform/plugins/admin/skills/file-presentation/SKILL.md +16 -33
  18. package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +1 -1
  19. package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +2 -2
  20. package/payload/platform/plugins/admin/skills/stream-log-review/references/analysis-patterns.md +2 -2
  21. package/payload/platform/plugins/anthropic/skills/get-api-key/SKILL.md +2 -4
  22. package/payload/platform/plugins/brochures/PLUGIN.md +1 -1
  23. package/payload/platform/plugins/buyers/PLUGIN.md +1 -2
  24. package/payload/platform/plugins/deep-research/skills/book-mirror/SKILL.md +1 -1
  25. package/payload/platform/plugins/deep-research/skills/strategic-reading/SKILL.md +1 -1
  26. package/payload/platform/plugins/docs/references/plugins-guide.md +2 -2
  27. package/payload/platform/plugins/email/references/email-reference.md +3 -7
  28. package/payload/platform/plugins/estate-business/PLUGIN.md +1 -2
  29. package/payload/platform/plugins/estate-coaching/PLUGIN.md +1 -2
  30. package/payload/platform/plugins/estate-onboarding/PLUGIN.md +1 -2
  31. package/payload/platform/plugins/estate-sales/PLUGIN.md +1 -2
  32. package/payload/platform/plugins/estate-teaching/PLUGIN.md +1 -2
  33. package/payload/platform/plugins/leads/PLUGIN.md +1 -2
  34. package/payload/platform/plugins/listings/PLUGIN.md +1 -2
  35. package/payload/platform/plugins/loop/PLUGIN.md +1 -1
  36. package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
  37. package/payload/platform/plugins/teaching/PLUGIN.md +2 -1
  38. package/payload/platform/plugins/vendors/PLUGIN.md +1 -2
  39. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +1 -1
  40. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  41. package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +1 -1
  42. package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +2 -8
  43. package/payload/platform/plugins/writer-craft/PLUGIN.md +2 -1
  44. package/payload/platform/scripts/component-knowledgedoc-backfill.ts +1 -1
  45. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +1 -1
  46. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  47. package/payload/platform/services/claude-session-manager/dist/http-server.js +45 -31
  48. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  49. package/payload/platform/services/claude-session-manager/dist/index.js +11 -0
  50. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  51. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.d.ts +30 -0
  52. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.d.ts.map +1 -0
  53. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.js +175 -0
  54. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.js.map +1 -0
  55. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +4 -2
  56. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
  57. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +15 -22
  58. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
  59. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +13 -0
  60. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  61. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +26 -4
  62. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  63. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts +9 -0
  64. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts.map +1 -1
  65. package/payload/platform/services/claude-session-manager/dist/session-store.js.map +1 -1
  66. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +10 -7
  67. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  68. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +51 -28
  69. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  70. package/payload/platform/services/claude-session-manager/scripts/onboarding-advance.sh +61 -0
  71. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-0.md +5 -0
  72. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-1.md +3 -0
  73. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-2.md +3 -0
  74. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-3.md +8 -0
  75. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-4.md +9 -0
  76. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-5.md +3 -0
  77. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-6.md +5 -0
  78. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-7.md +5 -0
  79. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-8.md +5 -0
  80. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-9.md +8 -0
  81. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-complete.md +3 -0
  82. package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-unreachable.md +3 -0
  83. package/payload/platform/templates/agents/public/IDENTITY.md +1 -1
  84. package/payload/platform/templates/specialists/agents/content-producer.md +3 -3
  85. package/payload/platform/templates/specialists/agents/personal-assistant.md +2 -2
  86. package/payload/premium-plugins/real-agent/BUNDLE.md +5 -5
  87. package/payload/premium-plugins/real-agent/agents/compliance.md +1 -1
  88. package/payload/premium-plugins/real-agent/agents/negotiator.md +1 -1
  89. package/payload/premium-plugins/real-agent/agents/valuer.md +1 -1
  90. package/payload/premium-plugins/real-agent/plugins/brochures/PLUGIN.md +1 -1
  91. package/payload/premium-plugins/real-agent/plugins/buyers/PLUGIN.md +1 -2
  92. package/payload/premium-plugins/real-agent/plugins/estate-business/PLUGIN.md +1 -2
  93. package/payload/premium-plugins/real-agent/plugins/estate-coaching/PLUGIN.md +1 -2
  94. package/payload/premium-plugins/real-agent/plugins/estate-onboarding/PLUGIN.md +1 -2
  95. package/payload/premium-plugins/real-agent/plugins/estate-sales/PLUGIN.md +1 -2
  96. package/payload/premium-plugins/real-agent/plugins/estate-teaching/PLUGIN.md +1 -2
  97. package/payload/premium-plugins/real-agent/plugins/leads/PLUGIN.md +1 -2
  98. package/payload/premium-plugins/real-agent/plugins/listings/PLUGIN.md +1 -2
  99. package/payload/premium-plugins/real-agent/plugins/loop/PLUGIN.md +1 -1
  100. package/payload/premium-plugins/real-agent/plugins/vendors/PLUGIN.md +1 -2
  101. package/payload/premium-plugins/teaching/PLUGIN.md +2 -1
  102. package/payload/premium-plugins/writer-craft/PLUGIN.md +2 -1
  103. package/payload/server/public/assets/{admin-CDvF5de6.js → admin-Bk2eXMFD.js} +24 -24
  104. package/payload/server/public/assets/{data-K_kS__sL.js → data-ll_OwVNL.js} +1 -1
  105. package/payload/server/public/assets/{graph-DeEigyO_.js → graph-DJ2VWioQ.js} +1 -1
  106. package/payload/server/public/assets/graph-labels-qhU8xZDH.js +1 -0
  107. package/payload/server/public/assets/{page-qSH972X0.js → page-Dk73ZO1F.js} +1 -1
  108. package/payload/server/public/assets/{page-B_rpjIRr.js → page-DsYsdBUK.js} +1 -1
  109. package/payload/server/public/data.html +3 -3
  110. package/payload/server/public/graph.html +3 -3
  111. package/payload/server/public/index.html +4 -4
  112. package/payload/server/server.js +121 -46
  113. package/payload/platform/plugins/admin/references/contextual-ui.md +0 -107
  114. package/payload/platform/scripts/__tests__/admin-persist-audit.test.ts +0 -182
  115. package/payload/server/public/assets/graph-labels-C7I5QvNv.js +0 -1
@@ -0,0 +1,85 @@
1
+ // Coverage for runInitLogging's success and failure branches via injected
2
+ // fs / stderr / exit impls. Task 062 (maxy-code).
3
+ //
4
+ // Brief asked for vitest. Codebase convention is node:test (matches
5
+ // apt-resolve.test.ts, peer-brand-detect.test.ts, etc); the package has no
6
+ // vitest dep and the test runner stanza in package.json invokes
7
+ // `node --test 'dist/__tests__/*.test.js'`.
8
+ import test from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { runInitLogging } from "../init-logging.js";
11
+ const LOG_DIR = "/tmp/test-task-062-logs";
12
+ const LOG_FILE = "/tmp/test-task-062-logs/install-2026-01-01T00-00-00-000Z.log";
13
+ const PERSIST_DIR = "/tmp/test-task-062-persist";
14
+ test("success path: header written via injected impls, no stderr, no exit", () => {
15
+ const appendCalls = [];
16
+ let stderrOut = "";
17
+ let exitCode = null;
18
+ runInitLogging({
19
+ logDir: LOG_DIR,
20
+ logFile: LOG_FILE,
21
+ persistDir: PERSIST_DIR,
22
+ headerLines: ["", "===", " Maxy Code Install Log", "==="],
23
+ mkdirSyncImpl: () => { },
24
+ appendFileSyncImpl: (p, d) => { appendCalls.push([p, d]); },
25
+ stderrWriteImpl: (c) => { stderrOut += c; },
26
+ exitImpl: (c) => { exitCode = c; },
27
+ });
28
+ assert.equal(appendCalls.length, 1);
29
+ assert.equal(appendCalls[0][0], LOG_FILE);
30
+ assert.equal(appendCalls[0][1], "\n===\n Maxy Code Install Log\n===\n");
31
+ assert.equal(stderrOut, "");
32
+ assert.equal(exitCode, null);
33
+ });
34
+ test("failure path: appendFileSync throws → stderr emits FAILED line, exit(1) called", () => {
35
+ let stderrOut = "";
36
+ let exitCode = null;
37
+ runInitLogging({
38
+ logDir: LOG_DIR,
39
+ logFile: LOG_FILE,
40
+ persistDir: PERSIST_DIR,
41
+ headerLines: ["a"],
42
+ mkdirSyncImpl: () => { },
43
+ appendFileSyncImpl: () => { throw new Error("EACCES: permission denied"); },
44
+ stderrWriteImpl: (c) => { stderrOut += c; },
45
+ exitImpl: (c) => { exitCode = c; },
46
+ });
47
+ assert.ok(stderrOut.includes("[create-maxy] init-logging FAILED reason=EACCES"), `stderr should contain init-logging FAILED line; got: ${JSON.stringify(stderrOut)}`);
48
+ assert.ok(stderrOut.includes(`log=${LOG_FILE}`));
49
+ assert.ok(stderrOut.includes(`persist=${PERSIST_DIR}`));
50
+ assert.equal(exitCode, 1);
51
+ });
52
+ test("failure path: mkdirSync throws → stderr emits FAILED line, exit(1) called", () => {
53
+ let stderrOut = "";
54
+ let exitCode = null;
55
+ let appendCalled = false;
56
+ runInitLogging({
57
+ logDir: LOG_DIR,
58
+ logFile: LOG_FILE,
59
+ persistDir: PERSIST_DIR,
60
+ headerLines: ["a"],
61
+ mkdirSyncImpl: () => { throw new Error("EROFS: read-only file system"); },
62
+ appendFileSyncImpl: () => { appendCalled = true; },
63
+ stderrWriteImpl: (c) => { stderrOut += c; },
64
+ exitImpl: (c) => { exitCode = c; },
65
+ });
66
+ assert.equal(appendCalled, false, "appendFileSync must not be invoked when mkdirSync throws");
67
+ assert.ok(stderrOut.includes("[create-maxy] init-logging FAILED reason=EROFS"));
68
+ assert.equal(exitCode, 1);
69
+ });
70
+ test("failure path: non-Error thrown value still surfaces as reason", () => {
71
+ let stderrOut = "";
72
+ let exitCode = null;
73
+ runInitLogging({
74
+ logDir: LOG_DIR,
75
+ logFile: LOG_FILE,
76
+ persistDir: PERSIST_DIR,
77
+ headerLines: ["a"],
78
+ mkdirSyncImpl: () => { },
79
+ appendFileSyncImpl: () => { throw "raw-string"; },
80
+ stderrWriteImpl: (c) => { stderrOut += c; },
81
+ exitImpl: (c) => { exitCode = c; },
82
+ });
83
+ assert.ok(stderrOut.includes("reason=raw-string"));
84
+ assert.equal(exitCode, 1);
85
+ });
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import { resolveInstallPortFromFs, buildMaxyUnitFile, buildClaudeSessionManagerU
7
7
  import { classifyOnboardingReadBack } from "./onboarding-readback.js";
8
8
  import { parseOsRelease, isUbuntuLike as isUbuntuLikePure, parseAptCacheCandidate, decideAptResolution, } from "./apt-resolve.js";
9
9
  import { findPeerBrandOnDefaultNeo4jPort } from "./peer-brand-detect.js";
10
+ import { runInitLogging } from "./init-logging.js";
10
11
  import { requireSupportedPlatform } from "./platform-detect.js";
11
12
  import { renderPlist } from "./launchd-plist.js";
12
13
  import { installAllBrewPackages } from "./brew-install.js";
@@ -82,15 +83,19 @@ const NPM_NET_FLAGS = [
82
83
  // Logging — timestamped to console AND persistent log file
83
84
  // ---------------------------------------------------------------------------
84
85
  function initLogging() {
85
- mkdirSync(LOG_DIR, { recursive: true });
86
- appendFileSync(LOG_FILE, [
87
- "",
88
- "=".repeat(64),
89
- ` ${BRAND.productName} Install Log — ${new Date().toISOString()}`,
90
- ` Node ${process.version} | ${process.platform} ${process.arch}`,
91
- "=".repeat(64),
92
- "",
93
- ].join("\n") + "\n");
86
+ runInitLogging({
87
+ logDir: LOG_DIR,
88
+ logFile: LOG_FILE,
89
+ persistDir: PERSIST_DIR,
90
+ headerLines: [
91
+ "",
92
+ "=".repeat(64),
93
+ ` ${BRAND.productName} Install Log — ${new Date().toISOString()}`,
94
+ ` Node ${process.version} | ${process.platform} ${process.arch}`,
95
+ "=".repeat(64),
96
+ "",
97
+ ],
98
+ });
94
99
  }
95
100
  function logFile(msg) {
96
101
  try {
@@ -3596,8 +3601,14 @@ if (PLATFORM === "darwin") {
3596
3601
  const PLATFORM_HEADER = `[create-maxy] platform=${PLATFORM} arch=${process.arch}` +
3597
3602
  (MACOS_VERSION ? ` macos=${MACOS_VERSION}` : ``) +
3598
3603
  ` version=${PKG_VERSION}`;
3599
- initLogging();
3604
+ // Task 062 — emit platform header and the resolved log/persist paths to
3605
+ // stdout BEFORE the log file is opened. If initLogging's mkdir or first
3606
+ // appendFileSync throws, the operator still sees brand/version/arch and the
3607
+ // exact path the log file SHOULD live at, instead of a silent exit between
3608
+ // the LOG_DIR mkdir and the first log write.
3600
3609
  console.log(PLATFORM_HEADER);
3610
+ console.log(`[create-maxy] log=${LOG_FILE} persist=${PERSIST_DIR}`);
3611
+ initLogging();
3601
3612
  console.log("================================================================");
3602
3613
  console.log(` ${BRAND.productName} — ${BRAND.tagline}. (${BRAND.hostname} v${PKG_VERSION})`);
3603
3614
  console.log("================================================================");
@@ -0,0 +1,28 @@
1
+ // Installer log-file initialization with diagnostic stderr emission on
2
+ // failure. Extracted from index.ts so the failure path can be covered by
3
+ // node:test without bringing in the rest of the installer's import-time side
4
+ // effects.
5
+ //
6
+ // Task 062 (maxy-code) — operator-observed silent exit between
7
+ // `mkdirSync(LOG_DIR)` and the first `appendFileSync(LOG_FILE, header)`.
8
+ // Both writes are guarded inside the same try/catch; a failure emits a
9
+ // literal `[create-maxy] init-logging FAILED reason=<msg> log=<path>
10
+ // persist=<path>` line to stderr and exits 1 so the operator always has a
11
+ // forensic surface regardless of whether the log file ever opened.
12
+ import { mkdirSync as realMkdirSync, appendFileSync as realAppendFileSync, } from "node:fs";
13
+ export function runInitLogging(opts) {
14
+ const mkdir = opts.mkdirSyncImpl ?? ((p, o) => { realMkdirSync(p, o); });
15
+ const append = opts.appendFileSyncImpl ?? ((p, d) => { realAppendFileSync(p, d); });
16
+ const stderr = opts.stderrWriteImpl ?? ((c) => { process.stderr.write(c); });
17
+ const exit = opts.exitImpl ?? ((c) => { process.exit(c); });
18
+ try {
19
+ mkdir(opts.logDir, { recursive: true });
20
+ append(opts.logFile, opts.headerLines.join("\n") + "\n");
21
+ }
22
+ catch (err) {
23
+ const msg = err instanceof Error ? err.message : String(err);
24
+ stderr(`[create-maxy] init-logging FAILED reason=${msg} log=${opts.logFile} persist=${opts.persistDir}\n`);
25
+ exit(1);
26
+ return;
27
+ }
28
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-realagent-code",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "Install Real Agent — Built for agents. By agents.",
5
5
  "bin": {
6
6
  "create-realagent-code": "./dist/index.js"
@@ -1,17 +1,16 @@
1
1
  /**
2
- * The component names that the platform UI treats as long-lived editor
3
- * surfaces (not single-shot prompts). They survive multiple `onSubmit`
4
- * fires because the operator may auto-save mid-edit, and they are also
5
- * the **server-side commitment surface** for Task 942: when the admin
6
- * agent emits one of these names via `render-component`, the live
7
- * stream-parser materialises the inline content as a sibling
8
- * `:KnowledgeDocument` artefact so the row appears in the artefacts
9
- * panel and survives session compaction.
2
+ * The historical persistent-component names. Pre-Task-066 the admin
3
+ * agent could emit a component tool_use carrying one of these names,
4
+ * and the live stream-parser materialised the inline content as a
5
+ * sibling `:KnowledgeDocument` artefact so the row appeared in the
6
+ * artefacts panel and survived session compaction.
10
7
  *
11
- * Single source of truth the platform UI (`app/admin-types.ts`) and
12
- * the admin MCP server (`plugins/admin/mcp/src/index.ts`) both import
13
- * from this module. Any drift here is an account-isolation /
14
- * persistence-doctrine bug; keep the constant in one place.
8
+ * The emitting tool is gone on native Claude Code (Task 066); the
9
+ * constant is preserved so the rehydration path can still recognise
10
+ * `:Component` rows written by older conversations. New conversations
11
+ * never produce these rows. The single-source-of-truth contract still
12
+ * holds: `app/admin-types.ts` and any consumer that needs to classify
13
+ * a stored component name imports from here.
15
14
  */
16
15
  export declare const PERSISTENT_COMPONENTS: Set<string>;
17
16
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,aAKhC,CAAC;AAEH;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAE9E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,aAKhC,CAAC;AAEH;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAE9E"}
@@ -3,19 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PERSISTENT_COMPONENTS = void 0;
4
4
  exports.isPersistentComponent = isPersistentComponent;
5
5
  /**
6
- * The component names that the platform UI treats as long-lived editor
7
- * surfaces (not single-shot prompts). They survive multiple `onSubmit`
8
- * fires because the operator may auto-save mid-edit, and they are also
9
- * the **server-side commitment surface** for Task 942: when the admin
10
- * agent emits one of these names via `render-component`, the live
11
- * stream-parser materialises the inline content as a sibling
12
- * `:KnowledgeDocument` artefact so the row appears in the artefacts
13
- * panel and survives session compaction.
6
+ * The historical persistent-component names. Pre-Task-066 the admin
7
+ * agent could emit a component tool_use carrying one of these names,
8
+ * and the live stream-parser materialised the inline content as a
9
+ * sibling `:KnowledgeDocument` artefact so the row appeared in the
10
+ * artefacts panel and survived session compaction.
14
11
  *
15
- * Single source of truth the platform UI (`app/admin-types.ts`) and
16
- * the admin MCP server (`plugins/admin/mcp/src/index.ts`) both import
17
- * from this module. Any drift here is an account-isolation /
18
- * persistence-doctrine bug; keep the constant in one place.
12
+ * The emitting tool is gone on native Claude Code (Task 066); the
13
+ * constant is preserved so the rehydration path can still recognise
14
+ * `:Component` rows written by older conversations. New conversations
15
+ * never produce these rows. The single-source-of-truth contract still
16
+ * holds: `app/admin-types.ts` and any consumer that needs to classify
17
+ * a stored component name imports from here.
19
18
  */
20
19
  exports.PERSISTENT_COMPONENTS = new Set([
21
20
  'action-list',
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAyBA,sDAEC;AA3BD;;;;;;;;;;;;;;GAcG;AACU,QAAA,qBAAqB,GAAG,IAAI,GAAG,CAAS;IACnD,aAAa;IACb,iBAAiB;IACjB,qBAAqB;IACrB,aAAa;CACd,CAAC,CAAC;AAEH;;GAEG;AACH,SAAgB,qBAAqB,CAAC,IAA+B;IACnE,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,6BAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACrE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAwBA,sDAEC;AA1BD;;;;;;;;;;;;;GAaG;AACU,QAAA,qBAAqB,GAAG,IAAI,GAAG,CAAS;IACnD,aAAa;IACb,iBAAiB;IACjB,qBAAqB;IACrB,aAAa;CACd,CAAC,CAAC;AAEH;;GAEG;AACH,SAAgB,qBAAqB,CAAC,IAA+B;IACnE,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,6BAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACrE,CAAC"}
@@ -1,17 +1,16 @@
1
1
  /**
2
- * The component names that the platform UI treats as long-lived editor
3
- * surfaces (not single-shot prompts). They survive multiple `onSubmit`
4
- * fires because the operator may auto-save mid-edit, and they are also
5
- * the **server-side commitment surface** for Task 942: when the admin
6
- * agent emits one of these names via `render-component`, the live
7
- * stream-parser materialises the inline content as a sibling
8
- * `:KnowledgeDocument` artefact so the row appears in the artefacts
9
- * panel and survives session compaction.
2
+ * The historical persistent-component names. Pre-Task-066 the admin
3
+ * agent could emit a component tool_use carrying one of these names,
4
+ * and the live stream-parser materialised the inline content as a
5
+ * sibling `:KnowledgeDocument` artefact so the row appeared in the
6
+ * artefacts panel and survived session compaction.
10
7
  *
11
- * Single source of truth the platform UI (`app/admin-types.ts`) and
12
- * the admin MCP server (`plugins/admin/mcp/src/index.ts`) both import
13
- * from this module. Any drift here is an account-isolation /
14
- * persistence-doctrine bug; keep the constant in one place.
8
+ * The emitting tool is gone on native Claude Code (Task 066); the
9
+ * constant is preserved so the rehydration path can still recognise
10
+ * `:Component` rows written by older conversations. New conversations
11
+ * never produce these rows. The single-source-of-truth contract still
12
+ * holds: `app/admin-types.ts` and any consumer that needs to classify
13
+ * a stored component name imports from here.
15
14
  */
16
15
  export const PERSISTENT_COMPONENTS = new Set<string>([
17
16
  'action-list',
@@ -911,9 +911,12 @@ FOR (tc:ToolCall) ON (tc.accountId, tc.startedAt);
911
911
  // ----------------------------------------------------------
912
912
  // Component — Task 815 admin-resume rehydration.
913
913
  //
914
- // Every render-component event emitted on an assistant turn
915
- // produces one Component node linked to its parent Message via
916
- // (Message)-[:HAS_COMPONENT]->(Component). The agent's narrative
914
+ // Each legacy component event emitted on an assistant turn (pre-066
915
+ // admin agent surface only) produced one Component node linked to its
916
+ // parent Message via (Message)-[:HAS_COMPONENT]->(Component). New
917
+ // conversations never write to this table; the rehydration path keeps
918
+ // the rows in place so historical conversations replay intact. The
919
+ // agent's narrative
917
920
  // text stays on Message.content; component name + data JSON +
918
921
  // position-relative-to-text live here. Sibling-node design (not
919
922
  // JSON-on-Message) avoids Neo4j's 128KB property soft-limit when
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "admin",
3
- "description": "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), brand-settings, account-manage, account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), render-component, session-reset, session-resume, file-attach, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform.",
3
+ "description": "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), brand-settings, account-manage, account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, file-attach, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform.",
4
4
  "version": "0.1.0",
5
5
  "author": {
6
6
  "name": "Rubytech LLC"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: admin
3
- description: "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), brand-settings, account-manage, account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), render-component, session-reset, session-resume, file-attach, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform."
3
+ description: "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), brand-settings, account-manage, account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, file-attach, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform."
4
4
  tools:
5
5
  - name: system-status
6
6
  publicAllowlist: false
@@ -32,8 +32,6 @@ tools:
32
32
  publicAllowlist: false
33
33
  - name: store-skill
34
34
  publicAllowlist: false
35
- - name: render-component
36
- publicAllowlist: false
37
35
  - name: session-reset
38
36
  publicAllowlist: false
39
37
  - name: session-resume
@@ -1,100 +1,103 @@
1
1
  #!/usr/bin/env bash
2
- # Task 038 Outcome E1 Stop-event hook that flags the admin agent's
3
- # onboarding-skill drift pattern: the prior /input carried an
4
- # `<onboarding-state>` block, but the latest assistant turn did not call
5
- # `render-component` AND did not end with a `?`-terminated sentence.
2
+ # Task 063 / 066. Stop-event hook that flags admin agent onboarding
3
+ # drift on the native Claude Code (PTY) surface. The contract is "the
4
+ # assistant ends its turn on a prompt the operator can answer" no
5
+ # named tool, no per-turn imperative, just a chat-surface end-state
6
+ # check.
6
7
  #
7
8
  # Behaviour:
8
- # 1. Read the transcript_path from the Stop event JSON on stdin.
9
+ # 1. Read transcript_path from the Stop event JSON on stdin.
9
10
  # 2. Walk the JSONL backwards. Find the most recent user event whose
10
- # `message.content` (string or text-block array) contains the literal
11
- # `<onboarding-state ` marker — that is the injection signature.
11
+ # `message.content` contains the literal `<onboarding-state ` marker.
12
12
  # 3. If no such user event exists in this transcript, fail-open (exit 0).
13
- # 4. Otherwise, find the most recent assistant event after that injection
14
- # event. Extract its tool-use names and the trailing text characters.
15
- # 5. If the tool-use list does NOT contain `render-component` AND the
16
- # trailing prose does NOT end with `?`, the agent has drifted — log
17
- # one `[precision-check] rule=onboarding-skill-drift` line to the
18
- # admin-side log directory and exit 0.
13
+ # 4. Otherwise, find the most recent assistant event after that injection.
14
+ # Count assistant turns since the injection so the log line carries a turn number.
15
+ # 5. Evaluate the ends-with-prompt contract on the assistant text:
16
+ # - PASS when the last non-whitespace character is `?`, OR
17
+ # - PASS when the trailing block (last ~8 lines) contains a numbered
18
+ # or bulleted list item (`1. `, `2) `, `- `, `* `).
19
+ # - FAIL otherwise.
20
+ # 6. Emit `[onboarding-skill-drift] turn=<N> contract=ends-with-prompt
21
+ # result=<pass|fail> reason=<reason>` on stderr.
19
22
  #
20
- # Exit 0 always this hook is observation, not enforcement. The
21
- # precision-check ledger increments via the log line; no retry is forced.
22
- # Infrastructure failures (jq missing, transcript unreadable) also exit 0
23
- # so a hook bug never blocks the agent's response.
23
+ # Exit 0 always: this hook is observation, not enforcement. Infrastructure
24
+ # failures (jq missing, transcript unreadable) also exit 0 so a hook bug
25
+ # never blocks the agent's response.
24
26
 
25
27
  set -uo pipefail
26
- LOG_TAG="[precision-check]"
28
+ LOG_TAG="[onboarding-skill-drift]"
27
29
 
28
- # fail-open if jq is unavailable — the transcript shape demands JSON parse
29
30
  if ! command -v jq >/dev/null 2>&1; then
30
- echo "$LOG_TAG rule=onboarding-skill-drift result=skip reason=jq-missing" >&2
31
+ echo "$LOG_TAG turn=0 contract=ends-with-prompt result=skip reason=jq-missing" >&2
31
32
  exit 0
32
33
  fi
33
34
 
34
35
  INPUT=$(cat 2>/dev/null || echo '{}')
35
36
  TRANSCRIPT=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
36
37
  if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
37
- echo "$LOG_TAG rule=onboarding-skill-drift result=skip reason=no-transcript" >&2
38
+ echo "$LOG_TAG turn=0 contract=ends-with-prompt result=skip reason=no-transcript" >&2
38
39
  exit 0
39
40
  fi
40
41
 
41
- # Locate the most recent injection signature in user events. The injection
42
- # block is prepended verbatim to the operator's input on the first /input
43
- # of an eligible session, so it appears as the leading bytes of a user
44
- # event's message text. Search backwards to find the last one in this
45
- # transcript window.
42
+ # Locate the most recent <onboarding-state ...> injection in a user event.
43
+ # The injection block is prepended verbatim to the operator's input on the
44
+ # first /input of an eligible session, so it appears as the leading bytes of
45
+ # the user event's message text. Search backwards for the last one.
46
46
  INJECTION_LINE=$(grep -n '<onboarding-state ' "$TRANSCRIPT" 2>/dev/null \
47
47
  | grep '"type":"user"' \
48
48
  | tail -n 1 \
49
49
  | cut -d: -f1)
50
50
 
51
51
  if [ -z "$INJECTION_LINE" ]; then
52
- # No injection in this transcript window nothing to enforce.
52
+ # No injection in this transcript window. Nothing to evaluate.
53
53
  exit 0
54
54
  fi
55
55
 
56
- # Slice the file from just after the injection line to the end; the
57
- # assistant's response sits in that suffix.
56
+ # Slice the file from just after the injection line.
58
57
  TAIL=$(tail -n "+$((INJECTION_LINE + 1))" "$TRANSCRIPT" 2>/dev/null)
59
58
 
60
- # Last assistant message in the suffix.
61
- ASSISTANT=$(echo "$TAIL" \
62
- | grep '"type":"assistant"' \
63
- | tail -n 1)
59
+ # Count assistant turns since the injection and pick the most recent.
60
+ ASSISTANT_TURNS=$(echo "$TAIL" | grep -c '"type":"assistant"')
61
+ ASSISTANT=$(echo "$TAIL" | grep '"type":"assistant"' | tail -n 1)
64
62
 
65
63
  if [ -z "$ASSISTANT" ]; then
66
- echo "$LOG_TAG rule=onboarding-skill-drift result=skip reason=no-assistant-turn" >&2
64
+ echo "$LOG_TAG turn=0 contract=ends-with-prompt result=skip reason=no-assistant-turn" >&2
67
65
  exit 0
68
66
  fi
69
67
 
70
- # Tool-use names from the assistant event.
71
- TOOL_USES=$(echo "$ASSISTANT" | jq -r '
72
- .message.content // []
73
- | map(select(.type == "tool_use") | .name)
74
- | join(",")
75
- ' 2>/dev/null)
76
-
77
- # Concatenated text content (final character matters for the `?` check).
68
+ # Concatenated text content (final character matters for the `?` check; final
69
+ # block matters for the enumerated-list check).
78
70
  ASSISTANT_TEXT=$(echo "$ASSISTANT" | jq -r '
79
71
  .message.content // []
80
72
  | map(select(.type == "text") | .text)
81
- | join(" ")
73
+ | join("\n")
82
74
  ' 2>/dev/null)
83
75
 
84
- HAS_RENDER_COMPONENT=0
85
- case "$TOOL_USES" in
86
- *render-component*) HAS_RENDER_COMPONENT=1 ;;
87
- esac
88
-
89
- # Trim trailing whitespace and check the last non-whitespace character.
76
+ # Trim trailing whitespace and find the last non-whitespace character.
90
77
  TRIMMED=$(echo "$ASSISTANT_TEXT" | sed -E 's/[[:space:]]+$//')
91
78
  LAST_CHAR=$(echo -n "$TRIMMED" | tail -c 1)
92
79
 
93
- if [ "$HAS_RENDER_COMPONENT" = "0" ] && [ "$LAST_CHAR" != "?" ]; then
94
- # Drift detected. Sample is the first 80 chars of the assistant text so
95
- # the log line tells the operator what shape of drift fired.
96
- SAMPLE=$(echo "$TRIMMED" | head -c 80 | tr '\n' ' ')
97
- echo "$LOG_TAG rule=onboarding-skill-drift result=block class=PRECISION sample='${SAMPLE}'" >&2
80
+ # Evaluate the ends-with-prompt contract.
81
+ ENDS_WITH_PROMPT=0
82
+ ENDS_WITH_PROMPT_REASON="neither-question-nor-list"
83
+
84
+ if [ "$LAST_CHAR" = "?" ]; then
85
+ ENDS_WITH_PROMPT=1
86
+ ENDS_WITH_PROMPT_REASON="trailing-question-mark"
87
+ else
88
+ # Take the last 8 lines and look for a numbered/bulleted list item.
89
+ TRAIL_LINES=$(echo "$TRIMMED" | tail -n 8)
90
+ if echo "$TRAIL_LINES" | grep -qE '^[[:space:]]*([0-9]+[.)]|[-*])[[:space:]]+'; then
91
+ ENDS_WITH_PROMPT=1
92
+ ENDS_WITH_PROMPT_REASON="trailing-enumerated-list"
93
+ fi
94
+ fi
95
+
96
+ if [ "$ENDS_WITH_PROMPT" = "1" ]; then
97
+ echo "$LOG_TAG turn=$ASSISTANT_TURNS contract=ends-with-prompt result=pass reason=$ENDS_WITH_PROMPT_REASON" >&2
98
+ else
99
+ SAMPLE=$(echo "$TRIMMED" | tail -c 120 | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g')
100
+ echo "$LOG_TAG turn=$ASSISTANT_TURNS contract=ends-with-prompt result=fail reason=$ENDS_WITH_PROMPT_REASON sample='${SAMPLE}'" >&2
98
101
  fi
99
102
 
100
103
  exit 0
@@ -10,7 +10,6 @@ import { execFileSync } from "node:child_process";
10
10
  import { appendFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
11
11
  import { writeKey, validateKey, hasKey, keyFilePath, deleteKey } from "../../../../lib/anthropic-key/dist/index.js";
12
12
  import { writeAdminEntry, removeAdminFromAccount } from "../../../../lib/admins-write/dist/index.js";
13
- import { isPersistentComponent } from "../../../../lib/persistent-components/dist/index.js";
14
13
  import { deviceUrlBlock } from "../../../../lib/device-url/dist/index.js";
15
14
  import { substituteBrandPlaceholders } from "../../../../lib/brand-templating/dist/index.js";
16
15
  import { resolveEntitlement } from "../../../../lib/entitlement/dist/index.js";
@@ -507,7 +506,7 @@ const PLUGIN_DISPLAY = {
507
506
  waitlist: { value: "waitlist", label: "waitlist", description: "Waitlist lifecycle — extract sign-ups, review, schedule automation." },
508
507
  replicate: { value: "replicate", label: "replicate", description: "Image generation — photorealistic, design, and fast draft images via Replicate. Enhances: content-producer, research-assistant" },
509
508
  };
510
- server.tool("onboarding-plugin-options", "Return the fully-assembled multi-select options array for onboarding Step 1. Classification (core/recommended/available) is derived from brand.json — the agent renders the result directly via render-component without further transformation.", {}, async () => {
509
+ server.tool("onboarding-plugin-options", "Return the fully-assembled plugin options array for onboarding Step 1. Classification (core/recommended/available) is derived from brand.json — the agent presents the entries to the operator as a numbered list in plain chat without further transformation.", {}, async () => {
511
510
  try {
512
511
  const brandPath = resolve(PLATFORM_ROOT, "config", "brand.json");
513
512
  if (!existsSync(brandPath)) {
@@ -1769,50 +1768,15 @@ eagerTool(server, "skill-load", "Load a plugin skill's SKILL.md body by skill na
1769
1768
  };
1770
1769
  }
1771
1770
  });
1772
- // Task 940`"rendered"` is a sentinel, not a persistence ack. The platform
1773
- // UI's stream-parser intercepts this tool_use upstream of the SDK reply
1774
- // (platform/ui/app/lib/claude-agent/stream-parser.ts:362) and emits a typed
1775
- // `component` event that admin-agent.ts's persistMessage flushes to Neo4j as
1776
- // a sibling :Component node.
1777
- //
1778
- // Task 942 — render-component is also the *server commitment surface* for
1779
- // PERSISTENT_COMPONENTS (action-list, document-editor, rich-content-editor,
1780
- // grid-editor). The actual file write + :KnowledgeDocument projection lives
1781
- // in the admin server process (stream-parser path) because the SDK delivers
1782
- // `tool_use.input` to the UI in the assistant message but the MCP handler's
1783
- // return reaches only the agent — there is no path back to persistMessage
1784
- // from here. This handler emits a one-line observability marker per
1785
- // persistence-eligible call so operators can grep for the commitment, and
1786
- // returns richer JSON so the doctrine grep
1787
- // `render-component.*"rendered"` resolves to a persistence-aware handler
1788
- // rather than a bare stub.
1789
- eagerTool(server, "render-component", "Render a pre-built UI component inline in the conversation. " +
1790
- "Call this instead of describing a UI — the component handles input collection. " +
1791
- "Wait for the user's response before continuing.", {
1792
- name: z.string().describe("Component name from the UI suite (e.g. single-select, confirm, info-card, form, progress)"),
1793
- data: z.record(z.string(), z.unknown()),
1794
- }, async ({ name, data }) => {
1795
- if (isPersistentComponent(name)) {
1796
- // Persistence-eligible: log the commitment so the live + audit grep
1797
- // (`[render-component-persist]`) can correlate the MCP call against
1798
- // the downstream :KnowledgeDocument MERGE. Byte counts come from
1799
- // data.html (preferred) or data.content; both being absent is a
1800
- // legitimate render of an empty editor and the projection is skipped
1801
- // server-side.
1802
- const html = typeof data?.html === "string" ? data.html : "";
1803
- const content = typeof data?.content === "string" ? data.content : "";
1804
- const bytes = html.length > 0 ? html.length : content.length;
1805
- const mimeType = html.length > 0 ? "text/html" : (content.length > 0 ? "text/markdown" : "");
1806
- console.error(`[render-component-persist] componentName=${name} bytes=${bytes} mimeType=${mimeType || "none"}`);
1807
- return {
1808
- content: [{
1809
- type: "text",
1810
- text: JSON.stringify({ rendered: true, name, persistent: true, bytes, mimeType }),
1811
- }],
1812
- };
1813
- }
1814
- return { content: [{ type: "text", text: "rendered" }] };
1815
- });
1771
+ // Task 066the inline UI-component tool is gone from the admin surface
1772
+ // on native Claude Code. The PTY runtime has no host bridge for the UI
1773
+ // primitives the legacy tool wrapped; calling it logged a "rendered"
1774
+ // sentinel that read like a successful render to the agent while the
1775
+ // chat surface saw nothing. Admin flows now use plain markdown for
1776
+ // reviewable artefacts (`skills/file-presentation/SKILL.md`) and
1777
+ // `file-attach` for downloadable artefacts. The :Component / :KnowledgeDocument
1778
+ // persistence pipeline is preserved for historical conversations that
1779
+ // already carry the rows; no new conversations write to it.
1816
1780
  eagerTool(server, "session-reset", "Reset the current session. Compacts conversation history to memory, clears the visible conversation, " +
1817
1781
  "and starts a fresh session with a new greeting. Call when the user asks to start a new session, " +
1818
1782
  "clear the conversation, or start fresh.", {}, async () => ({ content: [{ type: "text", text: "reset" }] }));
@@ -2984,10 +2948,11 @@ server.tool("premium-deliver", "Deliver a purchased premium plugin. Copies plugi
2984
2948
  // file-attach — copy a generated file into the attachment store for download
2985
2949
  // ---------------------------------------------------------------------------
2986
2950
  eagerTool(server, "file-attach", "Attach a file from the account directory for delivery to the user. " +
2987
- "Copies the file into the attachment store and returns metadata for rendering " +
2988
- "a download component. The file must be within the account directory tree. " +
2989
- "After calling this tool, call render-component with name 'file-attachment' " +
2990
- "and pass the returned metadata as data.", {
2951
+ "Copies the file into the attachment store and returns metadata the chat " +
2952
+ "surface uses to present the download. The file must be within the account " +
2953
+ "directory tree. The returned metadata is the operator-visible record of the " +
2954
+ "attachment; quote it back to the user in plain text so they have the filename " +
2955
+ "and size on screen.", {
2991
2956
  filePath: z.string().describe("Absolute path to the file to attach"),
2992
2957
  }, async ({ filePath }) => {
2993
2958
  const TAG = "[file-attach]";