@neuralis/brain-core 0.3.0 → 0.3.2

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 (123) hide show
  1. package/README.md +41 -40
  2. package/app/README.md +3 -3
  3. package/app/filesystem/FilesystemHost.tsx +7 -0
  4. package/app/filesystem/README.md +1 -1
  5. package/app/filesystem/api/index.ts +4 -4
  6. package/app/filesystem/hooks/README.md +1 -1
  7. package/app/filesystem/hooks/useFilesystemSSE.ts +41 -1
  8. package/app/filesystem/host/README.md +2 -2
  9. package/app/filesystem/host/buildFilesystemHostValue.ts +9 -2
  10. package/app/filesystem/lib/sourceKindForm.ts +109 -5
  11. package/app/filesystem/store/filesystemStore.ts +13 -3
  12. package/app/filesystem/ui/DiscoverDock.tsx +5 -1
  13. package/app/filesystem/ui/FileTree.tsx +34 -8
  14. package/app/filesystem/ui/FilesystemWidget.tsx +10 -2
  15. package/app/filesystem/ui/GitControlPanel.tsx +2 -2
  16. package/app/filesystem/ui/PendingChangesPanel.tsx +1 -1
  17. package/app/filesystem/ui/README.md +14 -13
  18. package/app/filesystem/ui/SourcesPanel.tsx +124 -24
  19. package/app/filesystem/ui/VectorHealthPanel.tsx +2 -2
  20. package/dist/app/host.js +41 -37
  21. package/dist/src/channels/filesystem.d.ts +1 -1
  22. package/dist/src/channels/filesystem.js +1 -1
  23. package/dist/src/domain/mime.d.ts +3 -3
  24. package/dist/src/domain/mime.js +3 -3
  25. package/dist/src/domain/scopeAccessCtx.d.ts +2 -2
  26. package/dist/src/domain/scopeAccessCtx.js +2 -2
  27. package/dist/src/domain/types.d.ts +8 -1
  28. package/dist/src/domain/types.d.ts.map +1 -1
  29. package/dist/src/events/pending.d.ts +1 -1
  30. package/dist/src/events/pending.js +1 -1
  31. package/dist/src/git/sourceGitControl.d.ts +2 -2
  32. package/dist/src/git/sourceGitControl.js +3 -3
  33. package/dist/src/lifecycle.d.ts +1 -1
  34. package/dist/src/routes/approve.d.ts +1 -1
  35. package/dist/src/routes/approve.js +1 -1
  36. package/dist/src/routes/events.d.ts +1 -1
  37. package/dist/src/routes/events.js +1 -1
  38. package/dist/src/routes/git.d.ts +7 -7
  39. package/dist/src/routes/git.js +18 -18
  40. package/dist/src/routes/health.d.ts +6 -6
  41. package/dist/src/routes/health.js +12 -12
  42. package/dist/src/routes/helpers/sourceNotConfigured.d.ts +1 -1
  43. package/dist/src/routes/helpers/sourceNotConfigured.js +1 -1
  44. package/dist/src/routes/helpers.d.ts +1 -1
  45. package/dist/src/routes/helpers.js +1 -1
  46. package/dist/src/routes/list.d.ts +1 -1
  47. package/dist/src/routes/list.js +1 -1
  48. package/dist/src/routes/pending.d.ts +4 -4
  49. package/dist/src/routes/pending.js +9 -9
  50. package/dist/src/routes/policies.d.ts +4 -4
  51. package/dist/src/routes/policies.d.ts.map +1 -1
  52. package/dist/src/routes/policies.js +11 -11
  53. package/dist/src/routes/policies.js.map +1 -1
  54. package/dist/src/routes/raw.d.ts +1 -1
  55. package/dist/src/routes/raw.js +1 -1
  56. package/dist/src/routes/read.d.ts +1 -1
  57. package/dist/src/routes/read.js +1 -1
  58. package/dist/src/routes/search.d.ts +1 -1
  59. package/dist/src/routes/search.js +1 -1
  60. package/dist/src/routes/sources.d.ts +20 -5
  61. package/dist/src/routes/sources.d.ts.map +1 -1
  62. package/dist/src/routes/sources.js +118 -118
  63. package/dist/src/routes/sources.js.map +1 -1
  64. package/dist/src/routes/upload.d.ts +1 -1
  65. package/dist/src/routes/upload.js +1 -1
  66. package/dist/src/routes/write.d.ts +2 -2
  67. package/dist/src/routes/write.js +2 -2
  68. package/dist/src/services/BridgeService.d.ts +7 -1
  69. package/dist/src/services/BridgeService.d.ts.map +1 -1
  70. package/dist/src/services/BridgeService.js +15 -3
  71. package/dist/src/services/BridgeService.js.map +1 -1
  72. package/dist/src/services/FilesystemService.d.ts +1 -1
  73. package/dist/src/services/FilesystemService.js +2 -2
  74. package/dist/src/services/declaredSources.d.ts +9 -3
  75. package/dist/src/services/declaredSources.d.ts.map +1 -1
  76. package/dist/src/services/declaredSources.js +2 -1
  77. package/dist/src/services/declaredSources.js.map +1 -1
  78. package/dist/src/tools/execCtx.d.ts +2 -2
  79. package/dist/src/tools/execCtx.js +2 -2
  80. package/dist/src/tools/fs_read.d.ts.map +1 -1
  81. package/dist/src/tools/fs_read.js +5 -0
  82. package/dist/src/tools/fs_read.js.map +1 -1
  83. package/dist/src/usecases/embeddingBacklog.js +1 -1
  84. package/dist/src/usecases/embeddingBacklog.js.map +1 -1
  85. package/dist/src/usecases/readFiles.d.ts.map +1 -1
  86. package/dist/src/usecases/readFiles.js +84 -1
  87. package/dist/src/usecases/readFiles.js.map +1 -1
  88. package/dist/src/usecases/syncPointWrites.d.ts +2 -0
  89. package/dist/src/usecases/syncPointWrites.d.ts.map +1 -1
  90. package/dist/src/usecases/syncPointWrites.js +5 -1
  91. package/dist/src/usecases/syncPointWrites.js.map +1 -1
  92. package/dist/src/usecases/vectorMaintenance.js +1 -1
  93. package/dist/src/usecases/vectorMaintenance.js.map +1 -1
  94. package/dist/src/usecases/write/content.js +1 -1
  95. package/dist/tools/fs_list.json +1 -1
  96. package/dist/tools/fs_read.json +1 -1
  97. package/dist/tools/fs_search.json +1 -1
  98. package/dist/tools/fs_write.json +1 -1
  99. package/instructions/brain-model.md +1 -1
  100. package/package.json +70 -63
  101. package/rules/brain-rules.md +2 -2
  102. package/skills/brain-start/SKILL.md +1 -1
  103. package/skills/connect-business-data/SKILL.md +1 -1
  104. package/skills/create-package/SKILL.md +8 -8
  105. package/skills/create-package/references/connectors-and-uri-policies.md +7 -2
  106. package/skills/create-package/references/runtime-and-build.md +1 -1
  107. package/skills/create-package/references/skills-and-context.md +1 -1
  108. package/skills/create-package/references/test-and-ship.md +4 -4
  109. package/skills/create-package/scripts/check-package.sh +1 -1
  110. package/skills/git/SKILL.md +23 -23
  111. package/skills/manage-files/SKILL.md +13 -13
  112. package/skills/manage-files/scripts/upload.sh +1 -1
  113. package/skills/manage-sources/SKILL.md +55 -54
  114. package/skills/manage-sources/scripts/reembed.sh +1 -1
  115. package/skills/manage-uri-policy/SKILL.md +9 -9
  116. package/skills/organize/SKILL.md +1 -1
  117. package/skills/sync/SKILL.md +2 -2
  118. package/tools/fs_list.json +1 -1
  119. package/tools/fs_read.json +1 -1
  120. package/tools/fs_search.json +1 -1
  121. package/tools/fs_write.json +1 -1
  122. package/workflows/sources-digest.json +1 -1
  123. package/docs/drive-overview.md +0 -59
@@ -30,7 +30,7 @@ import {
30
30
  type LucideIcon,
31
31
  } from 'lucide-react';
32
32
  import { meetsRequires } from '@neuralis/package-system/access';
33
- import { SkillLauncher } from '@neuralis/package-system/client';
33
+ import { SkillLauncher, createPackageApi } from '@neuralis/package-system/client';
34
34
  import { cn } from '../lib/cn';
35
35
  import {
36
36
  listSourceConfigs,
@@ -55,7 +55,7 @@ import {
55
55
  type DiscoverableSourceSuggestion,
56
56
  } from '../api';
57
57
  import { dimmedSourceTone, getSourceVisualTone, sourceLiveReasonLabel } from '../lib/treeVisualState';
58
- import { rootFieldMeta } from '../lib/sourceKindForm';
58
+ import { fieldControl, rootFieldMeta, selectOptionsFrom, type OptionsRoute, type SelectOption } from '../lib/sourceKindForm';
59
59
  import { resolveIconWithFallback, resolveLucideIcon } from '../lib/iconResolver';
60
60
  import { useFilesystemStore } from '../store/filesystemStore';
61
61
  import { fileKey, sourceFromUri, sourceRootUri } from '../store/helpers';
@@ -479,6 +479,8 @@ export function SourcesPanel(props: Props) {
479
479
  const setPendingSourceSuggestion = useFilesystemStore((s) => s.setPendingSourceSuggestion);
480
480
  const refresh = useFilesystemStore((s) => s.refresh);
481
481
  const sourceSummaries = useFilesystemStore((s) => s.sources);
482
+ // A `devices` frame bumps it: the device `select` re-reads its options.
483
+ const deviceRevision = useFilesystemStore((s) => s.deviceRevision);
482
484
 
483
485
  // Source-key suggestion inputs: the signed-in user (for the user-scope
484
486
  // qualifier) and the active agent's name (for the agent-scope qualifier).
@@ -489,13 +491,13 @@ export function SourcesPanel(props: Props) {
489
491
  // `recognizesPackages` toggle in the UI. Server is the enforcement floor; this
490
492
  // only HIDES the control. Undefined priority (no signal) ⇒ hide (deny-by-default).
491
493
  const isOwnerEffective = (host.currentRole?.priority ?? 99) <= 2;
492
- // warded-wren — affordance mirror for the `drive.mount` gate that now covers
494
+ // warded-wren — affordance mirror for the `sources.mount` gate that now covers
493
495
  // browse and the discoverable list. Same shape as `FileTree`'s: the ONE shared
494
496
  // kernel predicate, a copy at the boundary because the host keeps the grant
495
497
  // list readonly, and it authorizes NOTHING — the routes stay the boundary.
496
498
  // Hidden, never a disabled row (the S2 discipline).
497
499
  const canMount = useMemo(
498
- () => meetsRequires({ grantedFeatures: [...(host.grantedFeatures ?? [])] }, { features: ['drive.mount'] }),
500
+ () => meetsRequires({ grantedFeatures: [...(host.grantedFeatures ?? [])] }, { features: ['sources.mount'] }),
499
501
  [host.grantedFeatures],
500
502
  );
501
503
  const agentSlug = useMemo(() => {
@@ -609,8 +611,8 @@ export function SourcesPanel(props: Props) {
609
611
  const selectedKindNeedsPath = rootField.needed;
610
612
  // The container-plane affordances: the `/mounts` volume picker, the
611
613
  // `neuralis:mount` callout, and Browse (which readdirs in the SERVER process,
612
- // i.e. the wrong machine for a host-plane root — a host root's authority is
613
- // the operator's broker ceiling, not this process's view of the disk).
614
+ // i.e. the wrong machine for a host-plane root — that root lives on the paired
615
+ // device chosen below, bounded by that device's own allowlist).
614
616
  const showContainerPathChrome = selectedKindNeedsPath && rootField.container;
615
617
 
616
618
  /**
@@ -1420,7 +1422,7 @@ export function SourcesPanel(props: Props) {
1420
1422
  ? 'This connector does not need a local path. Choose the scope and URI scheme; the package owns the runtime binding.'
1421
1423
  : rootField.container
1422
1424
  ? 'Mount any folder from your system. The path must be accessible to the server process.'
1423
- : 'A path on the operator\u2019s host machine, reached through the host broker. It needs no volume mount here, and the broker ceiling still clamps what is reachable.'}
1425
+ : 'A folder on the device you choose below \u2014 your own paired machine. It needs no volume mount here; that device\u2019s own allowlist (neuralis device grant) still limits what is reachable.'}
1424
1426
  </div>
1425
1427
  )}
1426
1428
  <div className="space-y-2">
@@ -1466,23 +1468,41 @@ export function SourcesPanel(props: Props) {
1466
1468
  </div>
1467
1469
  {Object.entries(schemaObject(selectedKind).properties)
1468
1470
  .filter(([key]) => key !== 'root')
1469
- .map(([key, prop]) => (
1470
- <div key={key} className="space-y-1">
1471
- <div className="text-[9px] font-semibold uppercase tracking-wider text-white/35">
1472
- {(prop as any)?.['x-neuralis']?.ui?.label ?? key}
1471
+ .map(([key, prop]) => {
1472
+ const field = fieldControl(prop, selectedKind?.packageId ?? '');
1473
+ const setValue = (value: string) => setAddDraft((d) => ({
1474
+ ...d,
1475
+ config: { ...d.config, [key]: value },
1476
+ }));
1477
+ return (
1478
+ <div key={key} className="space-y-1">
1479
+ <div className="text-[9px] font-semibold uppercase tracking-wider text-white/35">
1480
+ {(prop as any)?.['x-neuralis']?.ui?.label ?? key}
1481
+ </div>
1482
+ {field.control === 'select' ? (
1483
+ <OptionsRouteSelect
1484
+ route={field.options}
1485
+ revision={deviceRevision}
1486
+ projectId={projectId}
1487
+ agentId={agentId}
1488
+ value={String(addDraft.config[key] ?? '')}
1489
+ onChange={setValue}
1490
+ />
1491
+ ) : (
1492
+ <input
1493
+ type={field.numeric ? 'number' : 'text'}
1494
+ value={String(addDraft.config[key] ?? '')}
1495
+ onChange={(e) => setValue(e.target.value)}
1496
+ placeholder={(prop as any)?.['x-neuralis']?.ui?.placeholder ?? (prop as any)?.description ?? key}
1497
+ className={inputCls}
1498
+ />
1499
+ )}
1500
+ {field.control === 'text' && field.refused ? (
1501
+ <div className="text-[9px] text-amber-300/80">{field.refused}</div>
1502
+ ) : null}
1473
1503
  </div>
1474
- <input
1475
- type={(prop as any).type === 'number' || (prop as any).type === 'integer' ? 'number' : 'text'}
1476
- value={String(addDraft.config[key] ?? '')}
1477
- onChange={(e) => setAddDraft((d) => ({
1478
- ...d,
1479
- config: { ...d.config, [key]: e.target.value },
1480
- }))}
1481
- placeholder={(prop as any)?.['x-neuralis']?.ui?.placeholder ?? (prop as any)?.description ?? key}
1482
- className={inputCls}
1483
- />
1484
- </div>
1485
- ))}
1504
+ );
1505
+ })}
1486
1506
  {/* Directory browser (non-Docker only) */}
1487
1507
  {showContainerPathChrome && !isDocker && browseOpen && (
1488
1508
  <div className="rounded-md bg-white/[0.03] border border-white/[0.1] overflow-hidden">
@@ -1887,7 +1907,7 @@ export function SourcesPanel(props: Props) {
1887
1907
  adjacent SkillLauncher hands the same uri-policy edit to
1888
1908
  the agent. Its visibility is DERIVED from the host's
1889
1909
  per-caller skill catalog (the `manage-uri-policy` skill
1890
- declares `drive.policy` itself) — never re-stated here. */}
1910
+ declares `sources.policy` itself) — never re-stated here. */}
1891
1911
  <div>
1892
1912
  <div className="flex items-center gap-1.5 py-1">
1893
1913
  <button type="button" onClick={() => toggleSubSection(config.source, 'perms')}
@@ -2355,3 +2375,83 @@ export function SourcesPanel(props: Props) {
2355
2375
  </div>
2356
2376
  );
2357
2377
  }
2378
+
2379
+ // ---------------------------------------------------------------------------
2380
+ // `select` field: options answered by a route in the DECLARING package
2381
+ // ---------------------------------------------------------------------------
2382
+
2383
+ /**
2384
+ * A dimmed option (an offline device): a muted text colour mixed OPAQUE into
2385
+ * the option's own surface, inline — the global `select option` rule outranks
2386
+ * any utility class, while an inline colour wins. Never `opacity`: the muted
2387
+ * colour is computed opaque against the same surface the option paints.
2388
+ */
2389
+ const DIMMED_OPTION_STYLE = {
2390
+ color: 'color-mix(in srgb, var(--control-text) 48%, rgb(var(--w-composer-bg-rgb, 30, 32, 38)))',
2391
+ } as const;
2392
+
2393
+ /**
2394
+ * A config field declared `x-neuralis.ui.control: "select"`. ONE request per
2395
+ * mount (and per project/agent change) to the declaring package's
2396
+ * `optionsRoute`, through that package's own route client — never this
2397
+ * package's — and ONE quiet re-read per `revision` bump (a burst of `devices`
2398
+ * frames): the shown list stays until the new answer replaces it. A failed
2399
+ * load is SHOWN, never an empty list; the list is UX only, the declaring
2400
+ * package re-checks the value when the source is created.
2401
+ */
2402
+ function OptionsRouteSelect({
2403
+ route,
2404
+ revision,
2405
+ projectId,
2406
+ agentId,
2407
+ value,
2408
+ onChange,
2409
+ }: {
2410
+ route: OptionsRoute;
2411
+ revision: number;
2412
+ projectId?: string | null;
2413
+ agentId: string;
2414
+ value: string;
2415
+ onChange: (value: string) => void;
2416
+ }) {
2417
+ const [options, setOptions] = useState<SelectOption[] | null>(null);
2418
+ const [loadError, setLoadError] = useState<string | null>(null);
2419
+ // The route is re-derived on every render; the effect keys on its VALUE.
2420
+ const { slug, path } = route;
2421
+ const query = new URLSearchParams(route.params).toString();
2422
+ // A new route/project/agent starts from "Loading…"; a revision bump does not.
2423
+ const scopeKey = `${slug}\n${path}\n${query}\n${projectId ?? ''}\n${agentId}`;
2424
+ const loadedScope = useRef<string | null>(null);
2425
+ useEffect(() => {
2426
+ let live = true;
2427
+ if (loadedScope.current !== scopeKey) {
2428
+ loadedScope.current = scopeKey;
2429
+ setOptions(null);
2430
+ }
2431
+ setLoadError(null);
2432
+ const api = createPackageApi({
2433
+ slug,
2434
+ getProjectId: () => projectId ?? null,
2435
+ getAgentId: () => agentId || null,
2436
+ });
2437
+ api.get<unknown>(path, Object.fromEntries(new URLSearchParams(query))).then(
2438
+ (answer) => { if (live) setOptions(selectOptionsFrom(answer)); },
2439
+ (err: unknown) => { if (live) setLoadError(err instanceof Error ? err.message : 'Could not load the options'); },
2440
+ );
2441
+ return () => { live = false; };
2442
+ }, [slug, path, query, projectId, agentId, scopeKey, revision]);
2443
+
2444
+ if (loadError) return <div className="text-[9px] text-red-300/80">{loadError}</div>;
2445
+ if (options === null) return <div className="text-[9px] text-white/30 animate-pulse">Loading…</div>;
2446
+ if (options.length === 0) return <div className="text-[9px] text-white/40 italic">Nothing to choose from.</div>;
2447
+ return (
2448
+ <select value={value} onChange={(e) => onChange(e.target.value)} className={selectCls}>
2449
+ <option value="" disabled>Choose…</option>
2450
+ {options.map((o) => (
2451
+ <option key={o.value} value={o.value} style={o.dimmed ? DIMMED_OPTION_STYLE : undefined}>
2452
+ {o.label}
2453
+ </option>
2454
+ ))}
2455
+ </select>
2456
+ );
2457
+ }
@@ -76,8 +76,8 @@ const ACTION_COPY: Record<ActionKind, { label: string; explainer: string; toolti
76
76
  export function VectorHealthPanel({ agentId, projectId }: Props) {
77
77
  const client = useFilesystemStore((s) => s.client);
78
78
  const host = useFilesystemHost();
79
- // A mirror only — the server's `drive.write` escalation is the gate.
80
- const canWrite = hasFeature({ grantedFeatures: [...(host.grantedFeatures ?? [])] }, 'drive.write');
79
+ // A mirror only — the server's `files.write` escalation is the gate.
80
+ const canWrite = hasFeature({ grantedFeatures: [...(host.grantedFeatures ?? [])] }, 'files.write');
81
81
  const [report, setReport] = useState<HealthReport | null>(null);
82
82
  /** Snapshot taken right BEFORE a destructive action runs, so we can show a
83
83
  * before/after delta on the stat cards once the post-action refetch lands. */