@growthub/cli 0.9.18 → 0.10.1

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 (56) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/reference-options/route.js +62 -0
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/refresh-sources/route.js +13 -2
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/resolver-templates/route.js +23 -0
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +35 -5
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/test-source/route.js +15 -1
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +2277 -0
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataTable.jsx +1 -0
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/FieldEditor.jsx +1 -0
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/FieldManager.jsx +9 -0
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ObjectSidebar.jsx +41 -0
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/RecordDrawer.jsx +1 -0
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ReferencePicker.jsx +244 -0
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxRunPanel.jsx +21 -0
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SourceTestPanel.jsx +15 -0
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/StatusPill.jsx +13 -0
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ToggleField.jsx +41 -0
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/dm-shared.jsx +99 -0
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/page.jsx +2 -1528
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +99 -6
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/connector-template-authoring.md +8 -0
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/data-model-reference-fields.md +15 -0
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/mcp-chrome-tool-connectors.md +12 -0
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/resolver-template-library.md +17 -0
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/source-resolver-registry.js +13 -0
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/README.md +12 -0
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/chrome-bridge.js +22 -0
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/custom-http.js +23 -0
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-commerce.js +22 -0
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-crm.js +23 -0
  30. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-project-management.js +22 -0
  31. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-spreadsheet.js +22 -0
  32. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/mcp-tool.js +22 -0
  33. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/template-registry.js +50 -0
  34. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/webhook.js +22 -0
  35. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/collect-reference-options.js +133 -0
  36. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/reference-resolver-registry.js +17 -0
  37. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolver-loader.js +6 -0
  38. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolvers/README.md +8 -0
  39. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolvers/local-data-model.js +11 -0
  40. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolvers/source-records.js +34 -0
  41. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/adapters/README.md +5 -3
  42. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-intelligence.js +203 -0
  43. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/index.js +1 -0
  44. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/field-contracts.js +81 -0
  45. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/reference-option-schema.js +59 -0
  46. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/reference-options.js +29 -0
  47. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +534 -23
  48. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +131 -1
  49. package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/export-training-traces.mjs +144 -0
  50. package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/grade-raw-pairs.mjs +279 -0
  51. package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/harvest-cursor-traces.mjs +288 -0
  52. package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/upload-graded-traces.mjs +128 -0
  53. package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +10 -0
  54. package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/seeded-configs/alignment-loop.config.json +264 -0
  55. package/dist/index.js +486 -1
  56. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10255,6 +10255,7 @@ var init_growthub_bridge_client = __esm({
10255
10255
  constructor(session = readActiveSession()) {
10256
10256
  this.session = session;
10257
10257
  }
10258
+ session;
10258
10259
  async listAssets(query = {}) {
10259
10260
  const url = bridgeUrl(this.session, "/api/cli/profile", {
10260
10261
  view: "gallery-assets",
@@ -11249,6 +11250,65 @@ var init_scaffold_session_memory = __esm({
11249
11250
  // src/starter/init.ts
11250
11251
  import fs58 from "node:fs";
11251
11252
  import path68 from "node:path";
11253
+ function readJsonFile2(filePath) {
11254
+ return JSON.parse(fs58.readFileSync(filePath, "utf8"));
11255
+ }
11256
+ function mergeDataModelObjects(baseObjects, seedObjects) {
11257
+ const readId = (value) => {
11258
+ const candidate = value.id;
11259
+ return typeof candidate === "string" ? candidate : "";
11260
+ };
11261
+ const merged = [...baseObjects];
11262
+ const indexById = /* @__PURE__ */ new Map();
11263
+ for (let i = 0; i < merged.length; i += 1) {
11264
+ const id = readId(merged[i]);
11265
+ if (id) indexById.set(id, i);
11266
+ }
11267
+ for (const seedObject of seedObjects) {
11268
+ const id = readId(seedObject);
11269
+ if (id && indexById.has(id)) {
11270
+ merged[indexById.get(id)] = {
11271
+ ...merged[indexById.get(id)],
11272
+ ...seedObject
11273
+ };
11274
+ continue;
11275
+ }
11276
+ merged.push(seedObject);
11277
+ if (id) indexById.set(id, merged.length - 1);
11278
+ }
11279
+ return merged;
11280
+ }
11281
+ function applySeededConfig(opts) {
11282
+ const seedSlug = opts.seedConfig.trim();
11283
+ if (!seedSlug) return;
11284
+ const seedPath = path68.join(opts.kitPath, "templates", "seeded-configs", `${seedSlug}.config.json`);
11285
+ if (!fs58.existsSync(seedPath)) {
11286
+ throw new Error(
11287
+ `Seeded config "${seedSlug}" was not found at ${seedPath}. Create templates/seeded-configs/<slug>.config.json in the starter kit first.`
11288
+ );
11289
+ }
11290
+ const outConfigPath = path68.join(opts.outPath, "apps", "workspace", "growthub.config.json");
11291
+ if (!fs58.existsSync(outConfigPath)) {
11292
+ throw new Error(`Expected workspace config at ${outConfigPath} while applying seeded config "${seedSlug}".`);
11293
+ }
11294
+ const baseConfig = readJsonFile2(outConfigPath);
11295
+ const seedConfig = readJsonFile2(seedPath);
11296
+ const mergedObjects = mergeDataModelObjects(
11297
+ Array.isArray(baseConfig.dataModel?.objects) ? baseConfig.dataModel.objects : [],
11298
+ Array.isArray(seedConfig.dataModel?.objects) ? seedConfig.dataModel.objects : []
11299
+ );
11300
+ const mergedConfig = {
11301
+ ...baseConfig,
11302
+ ...seedConfig,
11303
+ dataModel: {
11304
+ ...baseConfig.dataModel || {},
11305
+ ...seedConfig.dataModel || {},
11306
+ objects: mergedObjects
11307
+ }
11308
+ };
11309
+ fs58.writeFileSync(outConfigPath, `${JSON.stringify(mergedConfig, null, 2)}
11310
+ `, "utf8");
11311
+ }
11252
11312
  async function initStarterWorkspace(opts) {
11253
11313
  const kitId = opts.kitId ?? DEFAULT_STARTER_KIT_ID;
11254
11314
  const absOut = path68.resolve(opts.out);
@@ -11257,6 +11317,13 @@ async function initStarterWorkspace(opts) {
11257
11317
  }
11258
11318
  const info = getBundledKitSourceInfo(kitId);
11259
11319
  copyBundledKitSource(kitId, absOut);
11320
+ if (opts.seedConfig) {
11321
+ applySeededConfig({
11322
+ outPath: absOut,
11323
+ kitPath: info.assetRoot,
11324
+ seedConfig: opts.seedConfig
11325
+ });
11326
+ }
11260
11327
  const reg = registerKitFork({
11261
11328
  forkPath: absOut,
11262
11329
  kitId: info.id,
@@ -29569,6 +29636,329 @@ function cleanMarkdownResponse(text70) {
29569
29636
  return cleaned.trim();
29570
29637
  }
29571
29638
 
29639
+ // src/runtime/native-intelligence/tool-intent-policy.ts
29640
+ function contractForSlug(slug, contracts) {
29641
+ return contracts.find((c) => c.slug === slug);
29642
+ }
29643
+ function validateIntentInputAgainstContract(intent, contract) {
29644
+ const warnings = [];
29645
+ const allowedKeys = new Set(contract.inputs.map((i) => i.key));
29646
+ for (const key of Object.keys(intent.input)) {
29647
+ if (!allowedKeys.has(key)) {
29648
+ warnings.push(`stripped unknown field "${key}" on tool "${intent.toolSlug}"`);
29649
+ }
29650
+ }
29651
+ for (const field of contract.inputs) {
29652
+ if (!field.required) continue;
29653
+ const v = intent.input[field.key];
29654
+ if (v === void 0 || v === null || v === "") {
29655
+ warnings.push(`missing required contract field "${field.key}" for tool "${intent.toolSlug}"`);
29656
+ }
29657
+ }
29658
+ return warnings;
29659
+ }
29660
+ function validateLocalModelToolIntents(intents, policy, availableContracts) {
29661
+ const validated = [];
29662
+ const rejected = [];
29663
+ const warnings = [];
29664
+ if (policy.mode === "disabled") {
29665
+ for (const intent of intents) {
29666
+ rejected.push({
29667
+ intent,
29668
+ reasons: ["tool policy mode is disabled"]
29669
+ });
29670
+ }
29671
+ return { validated, rejected, warnings };
29672
+ }
29673
+ const allowed = new Set(policy.allowedToolSlugs.map((s) => s.trim()).filter(Boolean));
29674
+ const minConfidence = typeof policy.minConfidence === "number" ? policy.minConfidence : void 0;
29675
+ for (const intent of intents) {
29676
+ const reasons = [];
29677
+ const slug = String(intent.toolSlug || "").trim();
29678
+ if (!slug) {
29679
+ reasons.push("missing toolSlug");
29680
+ rejected.push({ intent, reasons });
29681
+ continue;
29682
+ }
29683
+ if (!allowed.has(slug)) {
29684
+ reasons.push(`toolSlug "${slug}" is not in allowedToolSlugs`);
29685
+ }
29686
+ const contract = contractForSlug(slug, availableContracts);
29687
+ if (!contract) {
29688
+ reasons.push(`no contract summary for slug "${slug}"`);
29689
+ }
29690
+ if (minConfidence !== void 0 && !(typeof intent.confidence === "number" && intent.confidence >= minConfidence)) {
29691
+ reasons.push(`confidence ${intent.confidence} below minimum ${minConfidence}`);
29692
+ }
29693
+ if (reasons.length) {
29694
+ rejected.push({ intent: { ...intent, toolSlug: slug }, reasons });
29695
+ continue;
29696
+ }
29697
+ const contractNonNull = contract;
29698
+ const fieldWarnings = validateIntentInputAgainstContract({ ...intent, toolSlug: slug }, contractNonNull);
29699
+ warnings.push(...fieldWarnings);
29700
+ const strippedInput = {};
29701
+ const allowedKeys = new Set(contractNonNull.inputs.map((i) => i.key));
29702
+ for (const [k, v] of Object.entries(intent.input)) {
29703
+ if (allowedKeys.has(k)) strippedInput[k] = v;
29704
+ }
29705
+ validated.push({
29706
+ ...intent,
29707
+ toolSlug: slug,
29708
+ input: strippedInput,
29709
+ warnings: fieldWarnings
29710
+ });
29711
+ }
29712
+ return { validated, rejected, warnings };
29713
+ }
29714
+
29715
+ // src/runtime/native-intelligence/sandbox-runner.ts
29716
+ var ENVELOPE_VERSION = "growthub-local-model-sandbox-v1";
29717
+ function traceLine(type, payload) {
29718
+ return JSON.stringify({ type, at: (/* @__PURE__ */ new Date()).toISOString(), ...payload });
29719
+ }
29720
+ function defaultToolPolicy(ctx) {
29721
+ return ctx.toolPolicy ?? {
29722
+ mode: "propose-only",
29723
+ allowedToolSlugs: ctx.allowedToolSlugs,
29724
+ requiresDeterministicValidation: true
29725
+ };
29726
+ }
29727
+ function buildSandboxSystemPrompt(input) {
29728
+ const ctx = input.context;
29729
+ const policy = defaultToolPolicy(ctx);
29730
+ const contractSummaries = ctx.availableContracts.map((c) => ({
29731
+ slug: c.slug,
29732
+ displayName: c.displayName,
29733
+ requiredBindings: c.requiredBindings,
29734
+ inputs: c.inputs.map((i) => ({ key: i.key, required: i.required, type: i.type }))
29735
+ }));
29736
+ const schemaHint = input.responseSchema ? `
29737
+ Optional response schema hint (follow when compatible):
29738
+ ${JSON.stringify(input.responseSchema)}
29739
+ ` : "";
29740
+ return [
29741
+ "You are Growthub Local Intelligence in a governed sandbox.",
29742
+ "Return a single JSON object only (no markdown fences).",
29743
+ "You must not claim to have executed tools or APIs.",
29744
+ "toolIntents are proposals only; include confidence between 0 and 1.",
29745
+ "",
29746
+ `taskId: ${ctx.taskId}`,
29747
+ `businessObjectType: ${input.businessObjectType}`,
29748
+ input.businessObjectId ? `businessObjectId: ${input.businessObjectId}` : "",
29749
+ `executionMode: ${ctx.executionMode}`,
29750
+ `toolPolicy.mode: ${policy.mode}`,
29751
+ `allowedToolSlugs: ${JSON.stringify(ctx.allowedToolSlugs)}`,
29752
+ "",
29753
+ "Available CMS / node contracts (summaries):",
29754
+ JSON.stringify(contractSummaries, null, 2),
29755
+ "",
29756
+ ctx.bindings ? `Current bindings:
29757
+ ${JSON.stringify(ctx.bindings)}
29758
+ ` : "",
29759
+ ctx.sourceRecordRefs?.length ? `sourceRecordRefs: ${JSON.stringify(ctx.sourceRecordRefs)}
29760
+ ` : "",
29761
+ schemaHint,
29762
+ "JSON shape:",
29763
+ "{",
29764
+ ' "text": string (optional concise narrative),',
29765
+ ' "json": object (optional structured payload),',
29766
+ ' "toolIntents": [{ "toolSlug", "reason", "input", "confidence" }],',
29767
+ ' "warnings": string[],',
29768
+ ' "confidence": number',
29769
+ "}"
29770
+ ].filter(Boolean).join("\n");
29771
+ }
29772
+ function buildSandboxUserPrompt(input) {
29773
+ return [`User intent:
29774
+ ${input.userIntent.trim()}`].join("\n\n");
29775
+ }
29776
+ function buildLocalIntelligenceSandboxPrompts(input) {
29777
+ return {
29778
+ systemPrompt: buildSandboxSystemPrompt(input),
29779
+ userPrompt: buildSandboxUserPrompt(input)
29780
+ };
29781
+ }
29782
+ function coerceToolIntents(raw) {
29783
+ if (!Array.isArray(raw)) return [];
29784
+ const out = [];
29785
+ for (const item of raw) {
29786
+ if (!item || typeof item !== "object") continue;
29787
+ const rec = item;
29788
+ const toolSlug = typeof rec.toolSlug === "string" ? rec.toolSlug.trim() : "";
29789
+ if (!toolSlug) continue;
29790
+ const reason = typeof rec.reason === "string" ? rec.reason : "";
29791
+ const inputObj = rec.input && typeof rec.input === "object" && !Array.isArray(rec.input) ? rec.input : {};
29792
+ const confidence = typeof rec.confidence === "number" && Number.isFinite(rec.confidence) ? rec.confidence : 0;
29793
+ out.push({ toolSlug, reason, input: inputObj, confidence });
29794
+ }
29795
+ return out;
29796
+ }
29797
+ function parseLocalModelSandboxResult(rawText) {
29798
+ const warnings = [];
29799
+ const trimmed = rawText.trim();
29800
+ if (!trimmed) {
29801
+ return { toolIntents: [], warnings: ["empty model response"], confidence: 0 };
29802
+ }
29803
+ let parsed;
29804
+ try {
29805
+ parsed = JSON.parse(trimmed);
29806
+ } catch {
29807
+ return {
29808
+ text: trimmed,
29809
+ toolIntents: [],
29810
+ warnings: ["model output was not valid JSON"],
29811
+ confidence: 0
29812
+ };
29813
+ }
29814
+ if (!parsed || typeof parsed !== "object") {
29815
+ return { toolIntents: [], warnings: ["parsed JSON was not an object"], confidence: 0 };
29816
+ }
29817
+ const obj = parsed;
29818
+ const text70 = typeof obj.text === "string" ? obj.text : void 0;
29819
+ const json = obj.json && typeof obj.json === "object" && !Array.isArray(obj.json) ? obj.json : void 0;
29820
+ const toolIntents = coerceToolIntents(obj.toolIntents);
29821
+ const w = Array.isArray(obj.warnings) ? obj.warnings.filter((w2) => typeof w2 === "string") : [];
29822
+ warnings.push(...w);
29823
+ const confidence = typeof obj.confidence === "number" && Number.isFinite(obj.confidence) ? obj.confidence : 0;
29824
+ return { text: text70, json, toolIntents, warnings, confidence };
29825
+ }
29826
+ function emptyEnvelope(input, config, adapterMode, message, fallback) {
29827
+ const result = {
29828
+ toolIntents: [],
29829
+ warnings: [message],
29830
+ confidence: 0
29831
+ };
29832
+ return {
29833
+ version: ENVELOPE_VERSION,
29834
+ taskId: input.taskId,
29835
+ businessObjectType: input.businessObjectType,
29836
+ businessObjectId: input.businessObjectId,
29837
+ adapter: {
29838
+ kind: "local-intelligence",
29839
+ mode: adapterMode,
29840
+ modelId: config.localModel ?? config.modelId,
29841
+ endpoint: config.endpoint
29842
+ },
29843
+ result,
29844
+ validatedToolIntents: [],
29845
+ rejectedToolIntents: [],
29846
+ rawText: "",
29847
+ latencyMs: 0,
29848
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
29849
+ trace: [traceLine("local-model-sandbox-run-completed", { taskId: input.taskId, ok: false, fallback })],
29850
+ fallback
29851
+ };
29852
+ }
29853
+ async function runLocalIntelligenceSandboxTask(input, config, options) {
29854
+ const adapterMode = String(input.adapterMode ?? "ollama");
29855
+ const toolPolicy = defaultToolPolicy(input.context);
29856
+ const trace = [];
29857
+ trace.push(traceLine("local-intelligence-adapter-selected", { kind: "local-intelligence", mode: adapterMode }));
29858
+ trace.push(traceLine("local-model-sandbox-run-started", { taskId: input.taskId }));
29859
+ const systemPrompt = buildSandboxSystemPrompt(input);
29860
+ const userPrompt = buildSandboxUserPrompt(input);
29861
+ const backend = options?.backend ?? createNativeIntelligenceBackend(config);
29862
+ let rawText = "";
29863
+ let latencyMs = 0;
29864
+ let resolvedModelId = config.localModel ?? config.modelId;
29865
+ try {
29866
+ const completion = await backend.complete({
29867
+ systemPrompt,
29868
+ userPrompt,
29869
+ temperature: config.defaultTemperature,
29870
+ maxTokens: config.defaultMaxTokens,
29871
+ responseFormat: "json",
29872
+ sandboxContext: input.context,
29873
+ toolPolicy
29874
+ });
29875
+ rawText = completion.text;
29876
+ latencyMs = completion.latencyMs;
29877
+ resolvedModelId = completion.modelId || resolvedModelId;
29878
+ } catch (err) {
29879
+ const message = err instanceof Error ? err.message : "model backend error";
29880
+ trace.push(traceLine("local-model-sandbox-run-completed", { taskId: input.taskId, ok: false, error: message }));
29881
+ const env = emptyEnvelope(input, config, adapterMode, message, true);
29882
+ env.trace = [...trace, ...env.trace ?? []];
29883
+ return env;
29884
+ }
29885
+ const parsed = parseLocalModelSandboxResult(rawText);
29886
+ for (const intent of parsed.toolIntents) {
29887
+ trace.push(traceLine("local-model-tool-intent-proposed", { toolSlug: intent.toolSlug, confidence: intent.confidence }));
29888
+ }
29889
+ const validation = validateLocalModelToolIntents(parsed.toolIntents, toolPolicy, input.context.availableContracts);
29890
+ for (const r of validation.rejected) {
29891
+ trace.push(traceLine("local-model-tool-intent-rejected", { toolSlug: r.intent.toolSlug, reasons: r.reasons }));
29892
+ }
29893
+ const mergedWarnings = [...parsed.warnings, ...validation.warnings];
29894
+ const result = {
29895
+ text: parsed.text,
29896
+ json: parsed.json,
29897
+ toolIntents: parsed.toolIntents,
29898
+ warnings: mergedWarnings,
29899
+ confidence: parsed.confidence
29900
+ };
29901
+ trace.push(traceLine("local-model-sandbox-run-completed", { taskId: input.taskId, ok: true }));
29902
+ return {
29903
+ version: ENVELOPE_VERSION,
29904
+ taskId: input.taskId,
29905
+ businessObjectType: input.businessObjectType,
29906
+ businessObjectId: input.businessObjectId,
29907
+ adapter: {
29908
+ kind: "local-intelligence",
29909
+ mode: adapterMode,
29910
+ modelId: resolvedModelId,
29911
+ endpoint: config.endpoint
29912
+ },
29913
+ result,
29914
+ validatedToolIntents: validation.validated,
29915
+ rejectedToolIntents: validation.rejected,
29916
+ rawText,
29917
+ latencyMs,
29918
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
29919
+ trace,
29920
+ fallback: false
29921
+ };
29922
+ }
29923
+
29924
+ // src/runtime/native-intelligence/source-record-export.ts
29925
+ import { createHash as createHash3 } from "node:crypto";
29926
+ function hashSystemPrompt(systemPrompt) {
29927
+ return createHash3("sha256").update(systemPrompt, "utf8").digest("hex");
29928
+ }
29929
+ function sandboxEnvelopeToTraceRecord(envelope, options) {
29930
+ return {
29931
+ version: "growthub-local-intelligence-trace-v1",
29932
+ taskId: envelope.taskId,
29933
+ businessObjectType: envelope.businessObjectType,
29934
+ businessObjectId: envelope.businessObjectId,
29935
+ modelId: envelope.adapter.modelId,
29936
+ systemPromptHash: hashSystemPrompt(options.systemPrompt),
29937
+ input: {
29938
+ userIntent: options.userIntent,
29939
+ availableContracts: options.availableContracts ?? [],
29940
+ bindings: envelope.result.json && typeof envelope.result.json === "object" && envelope.result.json !== null && "bindings" in envelope.result.json && typeof envelope.result.json.bindings === "object" ? envelope.result.json.bindings : void 0
29941
+ },
29942
+ output: {
29943
+ json: envelope.result.json,
29944
+ toolIntents: envelope.result.toolIntents,
29945
+ warnings: [
29946
+ ...envelope.result.warnings,
29947
+ ...envelope.validatedToolIntents?.flatMap((v) => v.warnings) ?? []
29948
+ ]
29949
+ },
29950
+ validation: {
29951
+ acceptedToolIntents: envelope.validatedToolIntents ?? [],
29952
+ rejectedToolIntents: envelope.rejectedToolIntents ?? []
29953
+ },
29954
+ createdAt: envelope.createdAt
29955
+ };
29956
+ }
29957
+ function formatTraceRecordLine(record) {
29958
+ return `${JSON.stringify(record)}
29959
+ `;
29960
+ }
29961
+
29572
29962
  // src/runtime/native-intelligence/index.ts
29573
29963
  function resolveConfigPath2() {
29574
29964
  return path50.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
@@ -35599,10 +35989,11 @@ async function runBrowseSkills(opts) {
35599
35989
  }
35600
35990
  function registerStarterCommands(program2) {
35601
35991
  const starter = program2.command("starter").description("Custom Workspace Starter Kit \u2014 scaffold or import a fork with full v1 Self-Healing Fork Sync wiring.");
35602
- starter.command("init").description("Scaffold a new custom workspace from the starter kit and auto-register it as a fork.").requiredOption("--out <path>", "Destination directory for the new workspace").option("--kit <kit-id>", `Source kit id (default: ${DEFAULT_STARTER_KIT_ID})`).option("--name <label>", "Human label for the fork").option("--upstream <owner/repo>", "Upstream GitHub repo \u2014 when set, also creates a remote fork").option("--destination-org <org>", "Create the GitHub fork under an org").option("--fork-name <name>", "Override the GitHub fork name").option("--remote-sync-mode <mode>", "Initial policy.remoteSyncMode \u2014 off|branch|pr (default: off)").option("--json", "Emit machine-readable output").action(async (opts) => {
35992
+ starter.command("init").description("Scaffold a new custom workspace from the starter kit and auto-register it as a fork.").requiredOption("--out <path>", "Destination directory for the new workspace").option("--kit <kit-id>", `Source kit id (default: ${DEFAULT_STARTER_KIT_ID})`).option("--seed-config <slug>", "Named seeded config from templates/seeded-configs/<slug>.config.json").option("--name <label>", "Human label for the fork").option("--upstream <owner/repo>", "Upstream GitHub repo \u2014 when set, also creates a remote fork").option("--destination-org <org>", "Create the GitHub fork under an org").option("--fork-name <name>", "Override the GitHub fork name").option("--remote-sync-mode <mode>", "Initial policy.remoteSyncMode \u2014 off|branch|pr (default: off)").option("--json", "Emit machine-readable output").action(async (opts) => {
35603
35993
  await runStarterInit({
35604
35994
  out: opts.out,
35605
35995
  kitId: opts.kit ?? DEFAULT_STARTER_KIT_ID,
35996
+ seedConfig: opts.seedConfig,
35606
35997
  name: opts.name,
35607
35998
  upstream: opts.upstream,
35608
35999
  destinationOrg: opts.destinationOrg,
@@ -39681,6 +40072,7 @@ async function runNativeIntelligenceHub() {
39681
40072
  { value: "models", label: "Manage local custom models", hint: "select active favorite/default model" },
39682
40073
  { value: "prompt", label: "Prompt local model (chat flow)", hint: "human first local prompt submissions" },
39683
40074
  { value: "flows", label: "Run native-intelligence with your prompt", hint: "planner/normalizer/recommender/summarizer" },
40075
+ { value: "sandbox", label: "Run sandboxed local model task", hint: "governed JSON + validated tool intents (no execution)" },
39684
40076
  { value: "marketing-context", label: "Marketing Context Builder", hint: "auto-draft product-marketing-context from project artifacts" },
39685
40077
  { value: "provider-config", label: "Configure API provider", hint: "Claude, OpenAI, Gemini, OpenRouter, or local" },
39686
40078
  { value: "__back_to_hub", label: "\u2190 Back to main menu" }
@@ -39794,6 +40186,10 @@ API key: ${result.apiKey ? "configured" : "not needed"}`,
39794
40186
  await runMarketingContextBuilder(baseUrl, defaultModel);
39795
40187
  continue;
39796
40188
  }
40189
+ if (action === "sandbox") {
40190
+ await runLocalIntelligenceSandboxDiscovery(baseUrl, defaultModel);
40191
+ continue;
40192
+ }
39797
40193
  const customPrompt = await p42.text({
39798
40194
  message: "Enter your local intelligence prompt",
39799
40195
  placeholder: "Describe what you want to create/analyze"
@@ -39807,6 +40203,95 @@ API key: ${result.apiKey ? "configured" : "not needed"}`,
39807
40203
  await runNativeIntelligenceFlowSuite(baseUrl, defaultModel, prompt);
39808
40204
  }
39809
40205
  }
40206
+ async function runLocalIntelligenceSandboxDiscovery(baseUrl, defaultModel) {
40207
+ const userIntent = await p42.text({
40208
+ message: "Describe the governed sandbox task",
40209
+ placeholder: "e.g. Inspect bindings and propose safe next steps"
40210
+ });
40211
+ if (p42.isCancel(userIntent)) return;
40212
+ const intent = String(userIntent).trim();
40213
+ if (!intent) {
40214
+ p42.note("Intent was empty.", "Sandboxed local model");
40215
+ return;
40216
+ }
40217
+ const boRaw = await p42.text({
40218
+ message: "Business object type (metadata label)",
40219
+ placeholder: "sandbox-environment",
40220
+ defaultValue: "sandbox-environment"
40221
+ });
40222
+ if (p42.isCancel(boRaw)) return;
40223
+ const businessObjectType = String(boRaw || "sandbox-environment").trim() || "sandbox-environment";
40224
+ const spinner15 = p42.spinner();
40225
+ spinner15.start("Loading contracts and running governed local model sandbox\u2026");
40226
+ try {
40227
+ const contracts = await loadRuntimeContracts();
40228
+ const allowed = contracts.map((c) => c.slug);
40229
+ const config = {
40230
+ ...readIntelligenceConfig(),
40231
+ backendType: "local",
40232
+ endpoint: `${baseUrl.replace(/\/$/, "")}/chat/completions`,
40233
+ localModel: defaultModel
40234
+ };
40235
+ const health = await checkBackendHealth(config);
40236
+ const taskId = `task_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
40237
+ const taskInput = {
40238
+ taskId,
40239
+ businessObjectType,
40240
+ userIntent: intent,
40241
+ context: {
40242
+ taskId,
40243
+ businessObjectType,
40244
+ executionMode: "local",
40245
+ allowedToolSlugs: allowed,
40246
+ availableContracts: contracts,
40247
+ toolPolicy: {
40248
+ mode: "propose-only",
40249
+ allowedToolSlugs: allowed,
40250
+ requiresDeterministicValidation: true,
40251
+ minConfidence: 0.15
40252
+ }
40253
+ },
40254
+ adapterMode: "ollama"
40255
+ };
40256
+ const envelope = await runLocalIntelligenceSandboxTask(taskInput, config);
40257
+ spinner15.stop(health.available ? "Sandbox run finished." : "Sandbox run finished (check backend health).");
40258
+ const tracePreview = (envelope.trace ?? []).slice(-8).join("\n");
40259
+ p42.note(
40260
+ [
40261
+ `adapter: ${envelope.adapter.mode} \xB7 model: ${envelope.adapter.modelId}`,
40262
+ `latencyMs: ${envelope.latencyMs}`,
40263
+ `confidence: ${envelope.result.confidence}`,
40264
+ `validated intents: ${envelope.validatedToolIntents?.length ?? 0} \xB7 rejected: ${envelope.rejectedToolIntents?.length ?? 0}`,
40265
+ envelope.fallback ? "Note: deterministic fallback envelope after backend error." : "",
40266
+ "",
40267
+ "Trace (recent):",
40268
+ tracePreview || "(none)",
40269
+ "",
40270
+ "Envelope JSON:",
40271
+ JSON.stringify(envelope, null, 2).slice(0, 12e3)
40272
+ ].filter(Boolean).join("\n"),
40273
+ "Governed local model sandbox"
40274
+ );
40275
+ const exportChoice = await p42.confirm({
40276
+ message: "Print one JSONL distillation trace line to copy?",
40277
+ initialValue: false
40278
+ });
40279
+ if (!p42.isCancel(exportChoice) && exportChoice) {
40280
+ const prompts = buildLocalIntelligenceSandboxPrompts(taskInput);
40281
+ const line = formatTraceRecordLine(
40282
+ sandboxEnvelopeToTraceRecord(envelope, {
40283
+ systemPrompt: prompts.systemPrompt,
40284
+ userIntent: intent,
40285
+ availableContracts: contracts.map((c) => ({ slug: c.slug, displayName: c.displayName }))
40286
+ })
40287
+ );
40288
+ p42.note(line.trimEnd(), "JSONL trace record");
40289
+ }
40290
+ } catch (err) {
40291
+ spinner15.stop("Sandbox run failed.");
40292
+ p42.note(err instanceof Error ? err.message : String(err), "Governed local model sandbox");
40293
+ }
40294
+ }
39810
40295
  async function runMarketingContextBuilder(baseUrl, model) {
39811
40296
  const projectDir = await p42.text({
39812
40297
  message: "Project directory to scan",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growthub/cli",
3
- "version": "0.9.18",
3
+ "version": "0.10.1",
4
4
  "description": "CLI control plane for Growthub Local and Agent Workspace as Code: export, fork, inspect, operate, sync, and optionally activate governed AI workspaces.",
5
5
  "type": "module",
6
6
  "bin": {