@amoree-code/ocean 0.0.0-stage → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +284 -2
- package/dist/application/auth/auth-orchestrator.js +77 -0
- package/dist/application/clients/clients.js +646 -0
- package/dist/application/clients/editors.js +398 -0
- package/dist/application/clients/hooks.js +335 -0
- package/dist/application/clients/native.js +410 -0
- package/dist/application/clients/opencode-plugin.js +158 -0
- package/dist/application/clients/registry.js +188 -0
- package/dist/application/gateway/webhook-gateway.js +137 -0
- package/dist/application/install/provider-installer.js +184 -0
- package/dist/application/integrations/charter-copies.js +76 -0
- package/dist/application/integrations/claude-vscode.js +106 -0
- package/dist/application/integrations/engine-entry-repair.js +105 -0
- package/dist/application/integrations/skill-filter.js +254 -0
- package/dist/application/layout/layout-apply.js +443 -0
- package/dist/application/layout/layout-plan.js +236 -0
- package/dist/application/layout/workspace-template.js +34 -0
- package/dist/application/onboarding/state.js +23 -0
- package/dist/application/onboarding/steps.js +249 -0
- package/dist/application/profiles/profile-distribution.js +53 -0
- package/dist/application/profiles/profile-suggestion.js +39 -0
- package/dist/application/profiles/profile-upgrade-service.js +208 -0
- package/dist/application/pull/history-index.js +476 -0
- package/dist/application/pull/project-match.js +132 -0
- package/dist/application/pull/pull.js +202 -0
- package/dist/application/pull/server-capture.js +307 -0
- package/dist/application/scheduler/local-scheduler.js +210 -0
- package/dist/composition/runtime.js +64 -0
- package/dist/engine-root.js +9 -0
- package/dist/infrastructure/filesystem/profile-loader.js +46 -0
- package/dist/infrastructure/filesystem/runtime-config-loader.js +29 -0
- package/dist/infrastructure/filesystem/skill-loader.js +84 -0
- package/dist/infrastructure/filesystem/workspace-capability.js +37 -0
- package/dist/infrastructure/observability/runtime-logger.js +16 -0
- package/dist/infrastructure/process/agent-tool-executor.js +99 -0
- package/dist/infrastructure/process/cli-process.js +49 -0
- package/dist/infrastructure/process/interactive-process.js +145 -0
- package/dist/infrastructure/process/process-supervisor.js +194 -0
- package/dist/infrastructure/process/service.js +252 -0
- package/dist/infrastructure/providers/browser-provider.js +2 -0
- package/dist/infrastructure/providers/claude-sdk.js +137 -0
- package/dist/infrastructure/providers/client-home.js +14 -0
- package/dist/infrastructure/providers/fake-browser-provider.js +146 -0
- package/dist/infrastructure/providers/ollama-embedder.js +74 -0
- package/dist/infrastructure/providers/openai-compatible.js +268 -0
- package/dist/infrastructure/providers/playwright-browser-provider.js +380 -0
- package/dist/infrastructure/providers/provider-capabilities.js +50 -0
- package/dist/infrastructure/providers/provider-invocation.js +184 -0
- package/dist/infrastructure/providers/provider-registry.js +77 -0
- package/dist/infrastructure/providers/providers.js +134 -0
- package/dist/infrastructure/wrappers/wrapper-manager.js +374 -0
- package/dist/interfaces/cli/auth-command.js +54 -0
- package/dist/interfaces/cli/browser-bridge-command.js +93 -0
- package/dist/interfaces/cli/browser-command.js +119 -0
- package/dist/interfaces/cli/capture-command.js +125 -0
- package/dist/interfaces/cli/client-test-command.js +133 -0
- package/dist/interfaces/cli/clients-command.js +129 -0
- package/dist/interfaces/cli/command-registry.js +1211 -0
- package/dist/interfaces/cli/context-command.js +172 -0
- package/dist/interfaces/cli/daily-command.js +131 -0
- package/dist/interfaces/cli/governance-command.js +55 -0
- package/dist/interfaces/cli/handoff-command.js +35 -0
- package/dist/interfaces/cli/help-command.js +207 -0
- package/dist/interfaces/cli/idea-command.js +47 -0
- package/dist/interfaces/cli/intercept-command.js +317 -0
- package/dist/interfaces/cli/layout-command.js +27 -0
- package/dist/interfaces/cli/market-command.js +52 -0
- package/dist/interfaces/cli/mcp-gateway-command.js +30 -0
- package/dist/interfaces/cli/memory-command.js +317 -0
- package/dist/interfaces/cli/migrate-command.js +26 -0
- package/dist/interfaces/cli/observe-command.js +78 -0
- package/dist/interfaces/cli/onboard-command.js +123 -0
- package/dist/interfaces/cli/operate-command.js +59 -0
- package/dist/interfaces/cli/profiles-command.js +70 -0
- package/dist/interfaces/cli/pull-command.js +54 -0
- package/dist/interfaces/cli/setup-command.js +143 -0
- package/dist/interfaces/cli/tasks-command.js +249 -0
- package/dist/main.js +49 -0
- package/dist/node-warnings.js +17 -0
- package/dist/version.js +8 -0
- package/market/mcp/chrome-devtools/ocean.capability.json +28 -0
- package/market/mcp/gateway/ocean.capability.json +21 -0
- package/market/mcp/obsidian/ocean.capability.json +21 -0
- package/market/mcp/ocean/ocean.capability.json +21 -0
- package/market/mcp/playwright/ocean.capability.json +23 -0
- package/node_modules/@ocean/brain/dist/brain/brain-eval.js +29 -0
- package/node_modules/@ocean/brain/dist/brain/brain-markdown.js +226 -0
- package/node_modules/@ocean/brain/dist/brain/brain-reindex.js +247 -0
- package/node_modules/@ocean/brain/dist/brain/brain-service.js +320 -0
- package/node_modules/@ocean/brain/dist/capture/inbox-sync.js +22 -0
- package/node_modules/@ocean/brain/dist/evidence/verification-report.js +13 -0
- package/node_modules/@ocean/brain/dist/infrastructure/brain/brain-index.js +285 -0
- package/node_modules/@ocean/brain/dist/memory/brain-dump.js +145 -0
- package/node_modules/@ocean/brain/dist/memory/daily-narrative.js +142 -0
- package/node_modules/@ocean/brain/dist/memory/index-sync.js +124 -0
- package/node_modules/@ocean/brain/dist/memory/lesson-activation.js +93 -0
- package/node_modules/@ocean/brain/dist/memory/lesson-learning.js +188 -0
- package/node_modules/@ocean/brain/dist/memory/lessons.js +321 -0
- package/node_modules/@ocean/brain/dist/memory/memory-audit.js +100 -0
- package/node_modules/@ocean/brain/dist/memory/memory-digest.js +49 -0
- package/node_modules/@ocean/brain/dist/memory/memory-harvest.js +345 -0
- package/node_modules/@ocean/brain/dist/memory/memory-paths.js +174 -0
- package/node_modules/@ocean/brain/dist/memory/memory-review.js +278 -0
- package/node_modules/@ocean/brain/dist/memory/memory-trash.js +152 -0
- package/node_modules/@ocean/brain/dist/memory/memory-write.js +216 -0
- package/node_modules/@ocean/brain/dist/memory/model-narrative.js +129 -0
- package/node_modules/@ocean/brain/dist/memory/profile-facts.js +103 -0
- package/node_modules/@ocean/brain/dist/memory/session-promotion.js +61 -0
- package/node_modules/@ocean/brain/dist/memory/session-summary.js +113 -0
- package/node_modules/@ocean/brain/dist/memory/signal-detection.js +66 -0
- package/node_modules/@ocean/brain/package.json +11 -0
- package/node_modules/@ocean/context/dist/context/context-compression.js +66 -0
- package/node_modules/@ocean/context/dist/context/context-cost.js +587 -0
- package/node_modules/@ocean/context/dist/context/context-ladder.js +363 -0
- package/node_modules/@ocean/context/dist/context/context-packet.js +213 -0
- package/node_modules/@ocean/context/dist/context/context-references.js +255 -0
- package/node_modules/@ocean/context/dist/context/intent-router.js +259 -0
- package/node_modules/@ocean/context/dist/context/resource-injection.js +36 -0
- package/node_modules/@ocean/context/package.json +11 -0
- package/node_modules/@ocean/core/dist/domain/brain/brain-record.js +22 -0
- package/node_modules/@ocean/core/dist/domain/brain/record-scope.js +22 -0
- package/node_modules/@ocean/core/dist/domain/capabilities/browser-contract.js +64 -0
- package/node_modules/@ocean/core/dist/domain/capabilities/capability-contract.js +16 -0
- package/node_modules/@ocean/core/dist/domain/capabilities/capability-manifest.js +72 -0
- package/node_modules/@ocean/core/dist/domain/context/context-validator.js +5 -0
- package/node_modules/@ocean/core/dist/domain/context/context.js +34 -0
- package/node_modules/@ocean/core/dist/domain/context/project-id.js +30 -0
- package/node_modules/@ocean/core/dist/domain/evidence/evidence.js +16 -0
- package/node_modules/@ocean/core/dist/domain/mcp/mcp-contract.js +36 -0
- package/node_modules/@ocean/core/dist/domain/mcp/memory-contract.js +41 -0
- package/node_modules/@ocean/core/dist/domain/ports/brain-index-port.js +7 -0
- package/node_modules/@ocean/core/dist/domain/ports/browser-port.js +8 -0
- package/node_modules/@ocean/core/dist/domain/ports/embedder-port.js +2 -0
- package/node_modules/@ocean/core/dist/domain/ports/platform-ports.js +2 -0
- package/node_modules/@ocean/core/dist/domain/ports/runtime-ports.js +2 -0
- package/node_modules/@ocean/core/dist/domain/ports/session-store-port.js +2 -0
- package/node_modules/@ocean/core/dist/domain/process/process-events.js +5 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-distribution.js +19 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-policy.js +34 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-selection.js +73 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-upgrade.js +123 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-validator.js +5 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile.js +218 -0
- package/node_modules/@ocean/core/dist/domain/profiles/runtime-config.js +30 -0
- package/node_modules/@ocean/core/dist/domain/providers/provider-capability.js +28 -0
- package/node_modules/@ocean/core/dist/domain/providers/provider.js +2 -0
- package/node_modules/@ocean/core/dist/domain/redaction/redaction.js +86 -0
- package/node_modules/@ocean/core/dist/domain/runs/agent-tools.js +92 -0
- package/node_modules/@ocean/core/dist/domain/runs/execution-plan.js +71 -0
- package/node_modules/@ocean/core/dist/domain/runs/run-contract.js +38 -0
- package/node_modules/@ocean/core/dist/domain/sessions/entry-contract.js +31 -0
- package/node_modules/@ocean/core/dist/domain/sessions/handoff.js +78 -0
- package/node_modules/@ocean/core/dist/domain/sessions/session.js +58 -0
- package/node_modules/@ocean/core/dist/domain/skills/default-skills.js +29 -0
- package/node_modules/@ocean/core/dist/domain/skills/skill-validator.js +5 -0
- package/node_modules/@ocean/core/dist/domain/skills/skill.js +11 -0
- package/node_modules/@ocean/core/dist/domain/tasks/task-id.js +85 -0
- package/node_modules/@ocean/core/dist/shared/fs-utils.js +212 -0
- package/node_modules/@ocean/core/dist/shared/local-date.js +16 -0
- package/node_modules/@ocean/core/dist/shared/paths.js +324 -0
- package/node_modules/@ocean/core/dist/shared/project-resolution.js +1065 -0
- package/node_modules/@ocean/core/dist/shared/task-reference.js +133 -0
- package/node_modules/@ocean/core/package.json +14 -0
- package/node_modules/@ocean/loop/dist/handoff/handoff-service.js +133 -0
- package/node_modules/@ocean/loop/dist/hooks/lifecycle-hooks.js +15 -0
- package/node_modules/@ocean/loop/dist/hooks/session-end-hook.js +183 -0
- package/node_modules/@ocean/loop/dist/hooks/session-start-hook.js +237 -0
- package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-store.js +690 -0
- package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-validator.js +5 -0
- package/node_modules/@ocean/loop/dist/loops/iteration-graph.js +147 -0
- package/node_modules/@ocean/loop/dist/loops/task-loop-record.js +2 -0
- package/node_modules/@ocean/loop/dist/loops/task-loop.js +281 -0
- package/node_modules/@ocean/loop/dist/operations/memory-knowledge-operations.js +263 -0
- package/node_modules/@ocean/loop/dist/operations/operation-contract.js +227 -0
- package/node_modules/@ocean/loop/dist/operations/operation-scope.js +28 -0
- package/node_modules/@ocean/loop/dist/operations/project-operations.js +93 -0
- package/node_modules/@ocean/loop/dist/operations/record-io.js +65 -0
- package/node_modules/@ocean/loop/dist/operations/record-operations.js +45 -0
- package/node_modules/@ocean/loop/dist/operations/task-linker.js +40 -0
- package/node_modules/@ocean/loop/dist/operations/task-operations.js +279 -0
- package/node_modules/@ocean/loop/dist/operations/write-guard.js +157 -0
- package/node_modules/@ocean/loop/dist/runs/prompt-assembly.js +112 -0
- package/node_modules/@ocean/loop/dist/runs/run-agent.js +425 -0
- package/node_modules/@ocean/loop/dist/runs/run-authorization.js +24 -0
- package/node_modules/@ocean/loop/dist/runs/tool-loop.js +173 -0
- package/node_modules/@ocean/loop/dist/sessions/session-closeout.js +150 -0
- package/node_modules/@ocean/loop/dist/sessions/session-pointer.js +133 -0
- package/node_modules/@ocean/loop/dist/sessions/session-projects.js +26 -0
- package/node_modules/@ocean/loop/dist/sessions/session-retention.js +132 -0
- package/node_modules/@ocean/loop/dist/tasks/archive-tasks.js +210 -0
- package/node_modules/@ocean/loop/dist/tasks/checkpoint-task.js +31 -0
- package/node_modules/@ocean/loop/dist/tasks/claim-task.js +126 -0
- package/node_modules/@ocean/loop/dist/tasks/list-tasks.js +113 -0
- package/node_modules/@ocean/loop/dist/tasks/new-task.js +42 -0
- package/node_modules/@ocean/loop/dist/tasks/renumber-tasks.js +134 -0
- package/node_modules/@ocean/loop/dist/tasks/verification-readiness.js +78 -0
- package/node_modules/@ocean/loop/dist/tasks/verify-task.js +234 -0
- package/node_modules/@ocean/loop/package.json +11 -0
- package/node_modules/@ocean/market/dist/adapters/mcp-adapter.js +64 -0
- package/node_modules/@ocean/market/dist/adapters/owned-directory.js +89 -0
- package/node_modules/@ocean/market/dist/adapters/plugin-adapter.js +39 -0
- package/node_modules/@ocean/market/dist/adapters/skill-adapter.js +32 -0
- package/node_modules/@ocean/market/dist/capability/adapter.js +2 -0
- package/node_modules/@ocean/market/dist/capability/catalog.js +63 -0
- package/node_modules/@ocean/market/dist/capability/content-digest.js +40 -0
- package/node_modules/@ocean/market/dist/capability/registry.js +81 -0
- package/node_modules/@ocean/market/dist/install/default-marketplace.js +47 -0
- package/node_modules/@ocean/market/dist/install/lock.js +34 -0
- package/node_modules/@ocean/market/dist/install/marketplace-service.js +165 -0
- package/node_modules/@ocean/market/dist/mcp/browser-bridge.js +263 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-connection.js +67 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-gateway.js +317 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-launch.js +102 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-runner.js +32 -0
- package/node_modules/@ocean/market/dist/security/description-scan.js +45 -0
- package/node_modules/@ocean/market/dist/security/permissions.js +57 -0
- package/node_modules/@ocean/market/dist/security/sandbox.js +74 -0
- package/node_modules/@ocean/market/dist/skills/core-skill-sync.js +113 -0
- package/node_modules/@ocean/market/dist/skills/signal-detection.js +84 -0
- package/node_modules/@ocean/market/dist/skills/skill-curation.js +123 -0
- package/node_modules/@ocean/market/dist/skills/skill-hub.js +220 -0
- package/node_modules/@ocean/market/dist/skills/task-observer.js +165 -0
- package/node_modules/@ocean/market/package.json +11 -0
- package/node_modules/@ocean/services/dist/browser/browser-service.js +124 -0
- package/node_modules/@ocean/services/dist/browser/browser-session.js +287 -0
- package/node_modules/@ocean/services/dist/browser/browser-task-runner.js +70 -0
- package/node_modules/@ocean/services/dist/doctor/workspace-doctor.js +547 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/call-ledger.js +114 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/mcp-client.js +162 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/obsidian-server.js +251 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/ocean-server.js +880 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/server-version.js +20 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/tool-registry.js +29 -0
- package/node_modules/@ocean/services/dist/obsidian/conflict-log.js +68 -0
- package/node_modules/@ocean/services/dist/obsidian/inbox-promotion.js +80 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-discovery.js +113 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-ingestion.js +77 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-sync.js +73 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-writer.js +75 -0
- package/node_modules/@ocean/services/package.json +11 -0
- package/ocean-marketplace.json +415 -0
- package/package.json +67 -4
- package/skills/core/business-logic/SKILL.md +22 -0
- package/skills/core/catch-up/SKILL.md +17 -0
- package/skills/core/core-thinking/SKILL.md +11 -0
- package/skills/core/design-thinking/SKILL.md +19 -0
- package/skills/core/graft/SKILL.md +156 -0
- package/skills/core/loop/SKILL.md +20 -0
- package/skills/core/session-handoff/SKILL.md +15 -0
- package/skills/core/use-browser/SKILL.md +69 -0
- package/skills/core/verification/SKILL.md +11 -0
- package/skills/index.json +56 -0
- package/templates/README.md +4 -0
- package/templates/profiles/default.json +51 -0
- package/templates/profiles/developer.json +51 -0
- package/templates/profiles/reviewer.json +51 -0
- package/templates/profiles/strategist.json +51 -0
- package/templates/workspace/00-inbox/INBOX.md +7 -0
- package/templates/workspace/00-inbox/candidates/.gitkeep +0 -0
- package/templates/workspace/00-inbox/native/.gitkeep +0 -0
- package/templates/workspace/01-daily/.gitkeep +0 -0
- package/templates/workspace/02-personal/CONFLICTS.md +7 -0
- package/templates/workspace/02-personal/MEMORY.md +15 -0
- package/templates/workspace/02-personal/goals/.gitkeep +0 -0
- package/templates/workspace/02-personal/identity/.gitkeep +0 -0
- package/templates/workspace/02-personal/preferences/.gitkeep +0 -0
- package/templates/workspace/03-professional/.gitkeep +0 -0
- package/templates/workspace/04-projects/PROJECTS.md +5 -0
- package/templates/workspace/04-projects/_template/docs/.gitkeep +0 -0
- package/templates/workspace/04-projects/_template/memory/.gitkeep +0 -0
- package/templates/workspace/04-projects/_template/tasks/.gitkeep +0 -0
- package/templates/workspace/05-knowledge/KNOWLEDGE.md +8 -0
- package/templates/workspace/05-knowledge/decisions/.gitkeep +0 -0
- package/templates/workspace/05-knowledge/lessons/.gitkeep +0 -0
- package/templates/workspace/06-templates/TEMPLATES.md +25 -0
- package/templates/workspace/06-templates/decision.md +8 -0
- package/templates/workspace/06-templates/intelligence.md +32 -0
- package/templates/workspace/06-templates/project-claude.md +48 -0
- package/templates/workspace/06-templates/session.md +38 -0
- package/templates/workspace/06-templates/task.md +42 -0
- package/templates/workspace/MANIFEST.json +51 -0
- package/templates/workspace/bridge/config/CONFIG.md +21 -0
- package/templates/workspace/bridge/config/runtime.json +4 -0
- package/templates/workspace/charter/core.md +104 -0
- package/templates/workspace/charter/policies/README.md +31 -0
- package/templates/workspace/charter/policies/claude-code-context.md +90 -0
- package/templates/workspace/charter/policies/concise-output.md +29 -0
- package/templates/workspace/charter/policies/context.md +91 -0
- package/templates/workspace/charter/policies/contracts/task.md +189 -0
- package/templates/workspace/charter/policies/git.md +60 -0
- package/templates/workspace/charter/policies/graph.md +47 -0
- package/templates/workspace/charter/policies/knowledge.md +33 -0
- package/templates/workspace/charter/policies/lifecycle.md +119 -0
- package/templates/workspace/charter/policies/memory.md +67 -0
- package/templates/workspace/charter/policies/minimal-change.md +43 -0
- package/templates/workspace/charter/policies/models.md +57 -0
- package/templates/workspace/charter/policies/privacy-terms.txt +3 -0
- package/templates/workspace/charter/policies/response.md +39 -0
- package/templates/workspace/charter/policies/skill-observer.md +44 -0
- package/templates/workspace/charter/policies/stack.md +23 -0
- package/templates/workspace/charter/policies/task.md +139 -0
- package/templates/workspace/charter/policies/verification.md +29 -0
- package/templates/workspace/sessions/.gitkeep +0 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { realpath, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { validateContextManifest } from "@ocean/core/domain/context/context-validator.js";
|
|
4
|
+
import { oceanRoot } from "@ocean/core/shared/paths.js";
|
|
5
|
+
import { knownTaskPrefixes, projectRules, readProjectManifest, resolveProject, } from "@ocean/core/shared/project-resolution.js";
|
|
6
|
+
import { resolveLadderRung } from "./context-ladder.js";
|
|
7
|
+
import { buildContextPacket } from "./context-packet.js";
|
|
8
|
+
import { classifyIntent } from "./intent-router.js";
|
|
9
|
+
// Lean context for headless runs: the prompt carries references (path + why), never file
|
|
10
|
+
// bodies. The provider is an agentic CLI with file access and reads a reference only if the
|
|
11
|
+
// request needs it. Two sources feed it: the profile's own contextSources and, when profile
|
|
12
|
+
// memory is enabled, the T-198 context packet for a request that names one exact record (a
|
|
13
|
+
// task id). Both go through the same realpath/allowedPaths boundary the old eager reader
|
|
14
|
+
// applied, so a headless prompt never points outside the profile's allowedPaths.
|
|
15
|
+
export const CONTEXT_REFERENCE_MAX_BYTES = 4_096;
|
|
16
|
+
// Within the context-ladder BUDGET_CEILING; the packet itself stats, never reads.
|
|
17
|
+
export const LEAN_PACKET_BUDGET = {
|
|
18
|
+
maxFiles: 5,
|
|
19
|
+
maxBytes: 200_000,
|
|
20
|
+
maxChars: 8_000,
|
|
21
|
+
maxOperationCost: 5,
|
|
22
|
+
};
|
|
23
|
+
const SECTION_HEADER = "## Context references\nBodies are not inlined; read a file only if the request needs it.";
|
|
24
|
+
async function sizeOf(file) {
|
|
25
|
+
try {
|
|
26
|
+
return (await stat(file)).size;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function canonicalBoundaries(allowedPaths, cwd) {
|
|
33
|
+
const boundaries = await Promise.all(allowedPaths.map(async (allowedPath) => {
|
|
34
|
+
try {
|
|
35
|
+
return await realpath(path.resolve(cwd, allowedPath));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
return boundaries.filter((boundary) => boundary !== null);
|
|
42
|
+
}
|
|
43
|
+
// The realpath/allowedPaths boundary carried over verbatim from the former eager context
|
|
44
|
+
// reader: a target that does not resolve, or whose canonical path (symlinks followed) lies
|
|
45
|
+
// outside every canonical allowedPath, is rejected. Returns the canonical path when allowed.
|
|
46
|
+
async function withinAllowedPaths(target, boundaries) {
|
|
47
|
+
let canonicalPath;
|
|
48
|
+
try {
|
|
49
|
+
canonicalPath = await realpath(target);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const allowed = boundaries.some((boundary) => canonicalPath === boundary ||
|
|
55
|
+
canonicalPath.startsWith(`${boundary}${path.sep}`));
|
|
56
|
+
return allowed ? canonicalPath : null;
|
|
57
|
+
}
|
|
58
|
+
// Accepted contextSources entries are stat'd, never read.
|
|
59
|
+
export async function resolveContextSources(profile, cwd) {
|
|
60
|
+
const references = [];
|
|
61
|
+
const omitted = [];
|
|
62
|
+
const boundaries = await canonicalBoundaries(profile.allowedPaths, cwd);
|
|
63
|
+
for (const relativePath of profile.contextSources) {
|
|
64
|
+
const canonicalPath = await withinAllowedPaths(path.resolve(cwd, relativePath), boundaries);
|
|
65
|
+
if (!canonicalPath) {
|
|
66
|
+
omitted.push(relativePath);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
references.push({
|
|
70
|
+
path: relativePath,
|
|
71
|
+
base: "cwd",
|
|
72
|
+
recordType: "context-source",
|
|
73
|
+
reason: "profile contextSources entry",
|
|
74
|
+
bytes: await sizeOf(canonicalPath),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return { references, omitted };
|
|
78
|
+
}
|
|
79
|
+
// References the context packet selects for the request. Only the exact-record rung (a task
|
|
80
|
+
// lookup naming one validated id) is used headlessly: memory/knowledge/decision lookups are
|
|
81
|
+
// keyword-triggered ("memory usage", "lessons", "best practice"), so they would point ordinary
|
|
82
|
+
// engineering prompts at private records. Every selected record must also lie inside the
|
|
83
|
+
// profile's allowedPaths (resolved against cwd); a rejected one goes to `omitted`. Unknown or
|
|
84
|
+
// low-confidence intents select nothing, and any error fails closed to no references, so a
|
|
85
|
+
// headless run never fails on a file it would not have read before.
|
|
86
|
+
export async function selectPacketReferences(prompt, cwd, allowedPaths) {
|
|
87
|
+
const references = [];
|
|
88
|
+
const omitted = [];
|
|
89
|
+
try {
|
|
90
|
+
const classification = classifyIntent(prompt, await knownTaskPrefixes());
|
|
91
|
+
if (resolveLadderRung(classification).rung !== "exact-record")
|
|
92
|
+
return { references, omitted };
|
|
93
|
+
const packet = await buildContextPacket(classification, LEAN_PACKET_BUDGET, cwd);
|
|
94
|
+
const boundaries = await canonicalBoundaries(allowedPaths, cwd);
|
|
95
|
+
for (const reference of packet.selectedReferences) {
|
|
96
|
+
const canonicalPath = await withinAllowedPaths(path.join(oceanRoot(), reference.sourcePath), boundaries);
|
|
97
|
+
if (!canonicalPath) {
|
|
98
|
+
omitted.push(reference.sourcePath);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
references.push({
|
|
102
|
+
path: reference.sourcePath,
|
|
103
|
+
base: "ocean-root",
|
|
104
|
+
recordType: reference.recordType,
|
|
105
|
+
reason: reference.selectionReason,
|
|
106
|
+
bytes: await sizeOf(canonicalPath),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return { references, omitted };
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return { references: [], omitted: [] };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// The absolute file a reference points at (what the provider must be able to read).
|
|
116
|
+
export function referenceTarget(reference, cwd) {
|
|
117
|
+
return reference.base === "cwd"
|
|
118
|
+
? path.resolve(cwd, reference.path)
|
|
119
|
+
: path.join(oceanRoot(), reference.path);
|
|
120
|
+
}
|
|
121
|
+
function inside(directory, root) {
|
|
122
|
+
return directory === root || directory.startsWith(`${root}${path.sep}`);
|
|
123
|
+
}
|
|
124
|
+
// The directories a workspace-restricted provider must be granted to open the references.
|
|
125
|
+
// Claude --add-dir and Gemini --include-directories grant a directory recursively, so a
|
|
126
|
+
// reference's canonical parent directory is granted only when that directory itself lies
|
|
127
|
+
// inside the profile's canonical allowedPaths: a single-file allowedPaths entry never widens
|
|
128
|
+
// to its siblings, and a symlinked entry grants its real location, not the link's folder.
|
|
129
|
+
// Directories inside the (canonical) cwd need no grant.
|
|
130
|
+
export async function referenceReadDirectories(references, allowedPaths, cwd) {
|
|
131
|
+
const boundaries = await canonicalBoundaries(allowedPaths, cwd);
|
|
132
|
+
let root;
|
|
133
|
+
try {
|
|
134
|
+
root = await realpath(cwd);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
root = path.resolve(cwd);
|
|
138
|
+
}
|
|
139
|
+
const directories = new Set();
|
|
140
|
+
for (const reference of references) {
|
|
141
|
+
const canonicalPath = await withinAllowedPaths(referenceTarget(reference, cwd), boundaries);
|
|
142
|
+
if (!canonicalPath)
|
|
143
|
+
continue;
|
|
144
|
+
const directory = path.dirname(canonicalPath);
|
|
145
|
+
if (inside(directory, root))
|
|
146
|
+
continue;
|
|
147
|
+
if (!(await withinAllowedPaths(directory, boundaries)))
|
|
148
|
+
continue;
|
|
149
|
+
directories.add(directory);
|
|
150
|
+
}
|
|
151
|
+
return [...directories];
|
|
152
|
+
}
|
|
153
|
+
function renderReference(reference) {
|
|
154
|
+
const target = reference.base === "cwd"
|
|
155
|
+
? reference.path
|
|
156
|
+
: path.join(oceanRoot(), reference.path);
|
|
157
|
+
const bytes = reference.bytes === null ? "?" : String(reference.bytes);
|
|
158
|
+
const reason = reference.reason.replace(/\s*\n\s*/g, " ");
|
|
159
|
+
return `- ${target} (${reference.recordType}, ${bytes} B): ${reason}`;
|
|
160
|
+
}
|
|
161
|
+
function renderProjectRules(rules) {
|
|
162
|
+
return [
|
|
163
|
+
`## Project rules (${rules.project})`,
|
|
164
|
+
...rules.rules.map((rule) => `- ${rule}`),
|
|
165
|
+
].join("\n");
|
|
166
|
+
}
|
|
167
|
+
// A bound project's rules ride in every headless run there, whatever the prompt or the profile's
|
|
168
|
+
// memory setting. Unreadable rules refuse the run (the error is not swallowed): a hard constraint
|
|
169
|
+
// is never silently dropped.
|
|
170
|
+
async function boundProjectRulesBlock(cwd) {
|
|
171
|
+
// A broken registry leaves no bound project to read rules from (the headless path never
|
|
172
|
+
// failed on it before); the session-start hook treats it the same way.
|
|
173
|
+
const resolution = await resolveProject(cwd).catch(() => null);
|
|
174
|
+
if (resolution?.status !== "bound")
|
|
175
|
+
return "";
|
|
176
|
+
const project = resolution.projectId;
|
|
177
|
+
let rules;
|
|
178
|
+
try {
|
|
179
|
+
const manifest = await readProjectManifest(project);
|
|
180
|
+
rules = manifest ? projectRules(manifest) : [];
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
throw new Error(`Project rules (${project}) unreadable: ${error instanceof Error ? error.message : String(error)} — fix 04-projects/${project}/project.json`);
|
|
184
|
+
}
|
|
185
|
+
return rules.length ? renderProjectRules({ project, rules }) : "";
|
|
186
|
+
}
|
|
187
|
+
export async function buildContextReferences(options) {
|
|
188
|
+
const { profile, prompt, cwd } = options;
|
|
189
|
+
const sources = await resolveContextSources(profile, cwd);
|
|
190
|
+
const packet = profile.memory.enabled
|
|
191
|
+
? await selectPacketReferences(prompt, cwd, profile.allowedPaths)
|
|
192
|
+
: { references: [], omitted: [] };
|
|
193
|
+
const rulesBlock = await boundProjectRulesBlock(cwd);
|
|
194
|
+
// The rules come first and are never squeezed out; the references fit in what is left.
|
|
195
|
+
const referenceBudget = rulesBlock
|
|
196
|
+
? CONTEXT_REFERENCE_MAX_BYTES - Buffer.byteLength(rulesBlock) - 2
|
|
197
|
+
: CONTEXT_REFERENCE_MAX_BYTES;
|
|
198
|
+
const seen = new Set();
|
|
199
|
+
const candidates = [...sources.references, ...packet.references].filter((reference) => {
|
|
200
|
+
const key = `${reference.base}:${reference.path}`;
|
|
201
|
+
if (seen.has(key))
|
|
202
|
+
return false;
|
|
203
|
+
seen.add(key);
|
|
204
|
+
return true;
|
|
205
|
+
});
|
|
206
|
+
const omitted = [...sources.omitted, ...packet.omitted];
|
|
207
|
+
const lines = [];
|
|
208
|
+
const references = [];
|
|
209
|
+
const overflow = [];
|
|
210
|
+
for (const reference of candidates) {
|
|
211
|
+
const line = renderReference(reference);
|
|
212
|
+
const next = [SECTION_HEADER, ...lines, line].join("\n");
|
|
213
|
+
if (overflow.length === 0 && Buffer.byteLength(next) <= referenceBudget) {
|
|
214
|
+
lines.push(line);
|
|
215
|
+
references.push(reference);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
overflow.push(reference);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (overflow.length) {
|
|
222
|
+
// Make room for the trailing note by dropping accepted lines if necessary.
|
|
223
|
+
const note = (count) => `- ... ${count} more references omitted (listed in the context_manifest event)`;
|
|
224
|
+
while (lines.length &&
|
|
225
|
+
Buffer.byteLength([SECTION_HEADER, ...lines, note(overflow.length)].join("\n")) > referenceBudget) {
|
|
226
|
+
lines.pop();
|
|
227
|
+
const dropped = references.pop();
|
|
228
|
+
if (dropped)
|
|
229
|
+
overflow.unshift(dropped);
|
|
230
|
+
}
|
|
231
|
+
lines.push(note(overflow.length));
|
|
232
|
+
omitted.push(...overflow.map((reference) => reference.path));
|
|
233
|
+
}
|
|
234
|
+
const referencesBlock = lines.length
|
|
235
|
+
? [SECTION_HEADER, ...lines].join("\n")
|
|
236
|
+
: "";
|
|
237
|
+
const content = [rulesBlock, referencesBlock].filter(Boolean).join("\n\n");
|
|
238
|
+
return {
|
|
239
|
+
content,
|
|
240
|
+
readDirectories: await referenceReadDirectories(references, profile.allowedPaths, cwd),
|
|
241
|
+
manifest: validateContextManifest({
|
|
242
|
+
files: references
|
|
243
|
+
.filter((reference) => reference.base === "cwd")
|
|
244
|
+
.map((reference) => reference.path),
|
|
245
|
+
bytes: Buffer.byteLength(content),
|
|
246
|
+
compactedSummary: null,
|
|
247
|
+
lastContextCheckpoint: new Date().toISOString(),
|
|
248
|
+
maxBytes: CONTEXT_REFERENCE_MAX_BYTES,
|
|
249
|
+
omitted,
|
|
250
|
+
compression: null,
|
|
251
|
+
references,
|
|
252
|
+
}),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=context-references.js.map
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// Deterministic intent router: pure text classification, no filesystem I/O, no model call,
|
|
2
|
+
// no MCP round trip. Runs before any retrieval so a caller (CLI, shim, hook, or MCP adapter)
|
|
3
|
+
// knows what Ocean operation — if any — a request maps to, without guessing an identifier or
|
|
4
|
+
// a project when the request does not name one clearly. See T-198 slice 4.
|
|
5
|
+
import { extractTaskIds, LEGACY_PREFIX, } from "@ocean/core/domain/tasks/task-id.js";
|
|
6
|
+
function matchesAny(text, patterns) {
|
|
7
|
+
return patterns.some((pattern) => pattern.test(text));
|
|
8
|
+
}
|
|
9
|
+
const TASK_COMPLETE = [
|
|
10
|
+
/\b(complete|close|finish|done|archive)\b/i,
|
|
11
|
+
/خلص|انهي|سكر|أرشف/,
|
|
12
|
+
];
|
|
13
|
+
const TASK_UPDATE = [/\b(update|edit|change)\b/i, /عدل|حدث|غير/];
|
|
14
|
+
const TASK_CONTINUE = [/\b(continue|resume)\b/i, /كمل|استمر/];
|
|
15
|
+
const TASK_CREATE = [
|
|
16
|
+
/\b(create|new|open)\b[^.\n]*\btask\b/i,
|
|
17
|
+
/\btask\b[^.\n]*\b(create|new|open)\b/i,
|
|
18
|
+
/تكت جديدة|انشئ تكت|سوي تكت|افتح تكت/,
|
|
19
|
+
];
|
|
20
|
+
const TASK_CREATE_NAME = /\b(?:called|named|title(?:d)?)(?:\s+is)?\s+["']?(.+?)["']?(?:[.!?]|$)/i;
|
|
21
|
+
const SAVE_VERB = [/\bsave\b/i, /احفظ|خزن|سجل/];
|
|
22
|
+
const REMEMBER_VERB = [/\b(remember|note|capture)\b/i, /تذكر/];
|
|
23
|
+
const REMEMBER_TARGET = [/\b(this|that)\b/i, /هذا|هاي|هذه/];
|
|
24
|
+
const AS_DECISION = [/\bdecision\b/i, /قرار/];
|
|
25
|
+
const AS_KNOWLEDGE = [/\bknowledge\b|\blesson\b/i, /معرفة|درس/];
|
|
26
|
+
const DECISION_LOOKUP = [
|
|
27
|
+
/\bwhat did we decide\b/i,
|
|
28
|
+
/\bwhy did we (choose|pick|decide)\b/i,
|
|
29
|
+
/\bdecision (about|on|for)\b/i,
|
|
30
|
+
/\bwhat was the decision\b/i,
|
|
31
|
+
/شنو قررنا|ليش اخترنا|ايش قررنا|القرار\s+(حول|بخصوص)/,
|
|
32
|
+
];
|
|
33
|
+
const WORK_STYLE = [
|
|
34
|
+
/\bwork[- ]style\b/i,
|
|
35
|
+
/\bhow (do|should) i (like to )?work\b/i,
|
|
36
|
+
/\bmy (working )?preferences\b/i,
|
|
37
|
+
/اسلوب العمل|طريقة (الشغل|العمل)/,
|
|
38
|
+
];
|
|
39
|
+
const PROJECT_CREATE = [
|
|
40
|
+
/\b(start|create|begin)\b[^.\n]*\b(new )?project\b/i,
|
|
41
|
+
/\bnew project\b/i,
|
|
42
|
+
/مشروع جديد|سوي مشروع|انشئ مشروع/,
|
|
43
|
+
];
|
|
44
|
+
const PROJECT_CREATE_NAME = /\b(?:called|named)\s+["']?([\w .-]+?)["']?(?:[.!?]|$)/i;
|
|
45
|
+
const PROJECT_DETECT = [
|
|
46
|
+
/\b(what|which) project\b/i,
|
|
47
|
+
/\bcurrent project\b/i,
|
|
48
|
+
/\bwhere am i\b/i,
|
|
49
|
+
/شنو المشروع|اي مشروع|وين انا/,
|
|
50
|
+
];
|
|
51
|
+
const KNOWLEDGE_LOOKUP = [
|
|
52
|
+
/\bknowledge\b/i,
|
|
53
|
+
/\blesson(s)?\b/i,
|
|
54
|
+
/\bwhat did we learn\b/i,
|
|
55
|
+
/\bbest practice\b/i,
|
|
56
|
+
/معرفة|درس|ايش تعلمنا/,
|
|
57
|
+
];
|
|
58
|
+
const MEMORY_LOOKUP = [
|
|
59
|
+
/\bremember\b/i,
|
|
60
|
+
/\brecall\b/i,
|
|
61
|
+
/\bmemory\b/i,
|
|
62
|
+
/\bwhat do you know about\b/i,
|
|
63
|
+
/تتذكر|ذاكرة/,
|
|
64
|
+
];
|
|
65
|
+
// "what tasks we have" / "check the tasks" / "what the next task": a list request with no id.
|
|
66
|
+
// Needs a task noun AND a list cue so "update all tasks" is not read as a list. Includes the
|
|
67
|
+
// typos seen in real requests (taks, tsks) and Arabic/Iraqi wording (شنو عدنه تكتات).
|
|
68
|
+
const TASK_NOUN = [/\b(tasks?|taks|tsks|tasts)\b/i, /تكت|تاسك|مهام|مهمة/];
|
|
69
|
+
const TASK_LIST_CUE = [
|
|
70
|
+
/\b(what|which|show|list|check|have|next|status|remaining|left|incomplete)\b/i,
|
|
71
|
+
/شنو|اي |اكو|عدن+ه?|باقي|الجاي|التالي|وين/,
|
|
72
|
+
];
|
|
73
|
+
const EXECUTE = [
|
|
74
|
+
/\b(run|execute|deploy|launch)\b/i,
|
|
75
|
+
/\bbuild\b/i,
|
|
76
|
+
/\bstart (the )?(server|service|app|build)\b/i,
|
|
77
|
+
/شغل|نفذ|ابني|شغّل/,
|
|
78
|
+
];
|
|
79
|
+
function safeResult(ambiguityReason) {
|
|
80
|
+
return {
|
|
81
|
+
intent: "unknown",
|
|
82
|
+
entityType: "unknown",
|
|
83
|
+
identifier: null,
|
|
84
|
+
action: "unknown",
|
|
85
|
+
confidence: "low",
|
|
86
|
+
ambiguityReason,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// taskPrefixes: the registered per-project prefixes, so "OL-001" is an id but "UTF-8" is not.
|
|
90
|
+
// The caller supplies them (the router stays pure and sync); the default is the legacy T.
|
|
91
|
+
export function classifyIntent(rawText, taskPrefixes = [LEGACY_PREFIX]) {
|
|
92
|
+
const text = (rawText ?? "").trim();
|
|
93
|
+
if (!text)
|
|
94
|
+
return safeResult("empty request");
|
|
95
|
+
const taskIds = extractTaskIds(text, taskPrefixes);
|
|
96
|
+
if (taskIds.length > 1) {
|
|
97
|
+
return safeResult(`multiple task identifiers found (${taskIds.join(", ")}); specify exactly one`);
|
|
98
|
+
}
|
|
99
|
+
if (taskIds.length === 1) {
|
|
100
|
+
const action = matchesAny(text, TASK_COMPLETE)
|
|
101
|
+
? "complete"
|
|
102
|
+
: matchesAny(text, TASK_UPDATE)
|
|
103
|
+
? "update"
|
|
104
|
+
: matchesAny(text, TASK_CONTINUE)
|
|
105
|
+
? "continue"
|
|
106
|
+
: "get";
|
|
107
|
+
return {
|
|
108
|
+
intent: "task-lookup",
|
|
109
|
+
entityType: "task",
|
|
110
|
+
identifier: taskIds[0],
|
|
111
|
+
action,
|
|
112
|
+
confidence: "high",
|
|
113
|
+
ambiguityReason: null,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (matchesAny(text, TASK_CREATE)) {
|
|
117
|
+
const titleMatch = TASK_CREATE_NAME.exec(text);
|
|
118
|
+
const identifier = titleMatch?.[1]?.trim() || null;
|
|
119
|
+
return {
|
|
120
|
+
intent: "task-create",
|
|
121
|
+
entityType: "task",
|
|
122
|
+
identifier,
|
|
123
|
+
action: "create",
|
|
124
|
+
confidence: identifier ? "high" : "medium",
|
|
125
|
+
ambiguityReason: identifier
|
|
126
|
+
? null
|
|
127
|
+
: "no task title captured ('called <title>' / 'named <title>')",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const hasSaveVerb = matchesAny(text, SAVE_VERB);
|
|
131
|
+
const hasRememberVerb = matchesAny(text, REMEMBER_VERB);
|
|
132
|
+
const hasTarget = matchesAny(text, REMEMBER_TARGET) ||
|
|
133
|
+
matchesAny(text, AS_DECISION) ||
|
|
134
|
+
matchesAny(text, AS_KNOWLEDGE);
|
|
135
|
+
// "remember"/"note" only count as a save command when they have an explicit target
|
|
136
|
+
// ("this", "that", "as a decision", ...); bare "remember" (e.g. "what do you remember
|
|
137
|
+
// about X") is a recall question, handled below by MEMORY_LOOKUP instead.
|
|
138
|
+
if (hasSaveVerb || (hasRememberVerb && hasTarget)) {
|
|
139
|
+
const entityType = matchesAny(text, AS_DECISION)
|
|
140
|
+
? "decision"
|
|
141
|
+
: matchesAny(text, AS_KNOWLEDGE)
|
|
142
|
+
? "knowledge"
|
|
143
|
+
: "memory";
|
|
144
|
+
return {
|
|
145
|
+
intent: "remember",
|
|
146
|
+
entityType,
|
|
147
|
+
identifier: null,
|
|
148
|
+
action: "remember",
|
|
149
|
+
confidence: hasTarget ? "high" : "medium",
|
|
150
|
+
ambiguityReason: hasTarget
|
|
151
|
+
? null
|
|
152
|
+
: "no explicit content target ('this'/'that') named for the save",
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (matchesAny(text, DECISION_LOOKUP)) {
|
|
156
|
+
return {
|
|
157
|
+
intent: "decision-lookup",
|
|
158
|
+
entityType: "decision",
|
|
159
|
+
identifier: null,
|
|
160
|
+
action: "lookup",
|
|
161
|
+
confidence: "high",
|
|
162
|
+
ambiguityReason: null,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (matchesAny(text, WORK_STYLE)) {
|
|
166
|
+
return {
|
|
167
|
+
intent: "work-style-lookup",
|
|
168
|
+
entityType: "work-style",
|
|
169
|
+
identifier: null,
|
|
170
|
+
action: "lookup",
|
|
171
|
+
confidence: "high",
|
|
172
|
+
ambiguityReason: null,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (matchesAny(text, PROJECT_CREATE)) {
|
|
176
|
+
const nameMatch = PROJECT_CREATE_NAME.exec(text);
|
|
177
|
+
const identifier = nameMatch ? nameMatch[1].trim() : null;
|
|
178
|
+
return {
|
|
179
|
+
intent: "project-create",
|
|
180
|
+
entityType: "project",
|
|
181
|
+
identifier,
|
|
182
|
+
action: "create",
|
|
183
|
+
confidence: identifier ? "high" : "medium",
|
|
184
|
+
ambiguityReason: identifier
|
|
185
|
+
? null
|
|
186
|
+
: "no project name captured ('called <name>' / 'named <name>')",
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (matchesAny(text, PROJECT_DETECT)) {
|
|
190
|
+
return {
|
|
191
|
+
intent: "project-detect",
|
|
192
|
+
entityType: "project",
|
|
193
|
+
identifier: null,
|
|
194
|
+
action: "lookup",
|
|
195
|
+
confidence: "high",
|
|
196
|
+
ambiguityReason: null,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
if (matchesAny(text, KNOWLEDGE_LOOKUP)) {
|
|
200
|
+
return {
|
|
201
|
+
intent: "knowledge-lookup",
|
|
202
|
+
entityType: "knowledge",
|
|
203
|
+
identifier: null,
|
|
204
|
+
action: "search",
|
|
205
|
+
confidence: "high",
|
|
206
|
+
ambiguityReason: null,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (matchesAny(text, MEMORY_LOOKUP)) {
|
|
210
|
+
return {
|
|
211
|
+
intent: "memory-lookup",
|
|
212
|
+
entityType: "memory",
|
|
213
|
+
identifier: null,
|
|
214
|
+
action: "search",
|
|
215
|
+
confidence: "high",
|
|
216
|
+
ambiguityReason: null,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
// No digit anywhere: "show me task 123" / "show TASK-123" name a malformed id, and the
|
|
220
|
+
// fail-closed rule says never to turn that into a list.
|
|
221
|
+
if (!/\d/.test(text) &&
|
|
222
|
+
matchesAny(text, TASK_NOUN) &&
|
|
223
|
+
matchesAny(text, TASK_LIST_CUE)) {
|
|
224
|
+
return {
|
|
225
|
+
intent: "task-lookup",
|
|
226
|
+
entityType: "task",
|
|
227
|
+
identifier: null,
|
|
228
|
+
action: "list",
|
|
229
|
+
confidence: "medium",
|
|
230
|
+
ambiguityReason: "task list request without an explicit task id; list the project's tasks, do not guess one",
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// "continue"/"resume" is checked before the execute verbs on purpose: in Arabic "شغل" is
|
|
234
|
+
// both the noun "work" and the verb "run", so "كمل شغل اللوكين" ("continue the login
|
|
235
|
+
// work") would otherwise be read as an execution request. Continue wording wins and the
|
|
236
|
+
// result stays medium-confidence and ambiguous, which is the fail-closed direction.
|
|
237
|
+
if (matchesAny(text, TASK_CONTINUE)) {
|
|
238
|
+
return {
|
|
239
|
+
intent: "task-lookup",
|
|
240
|
+
entityType: "task",
|
|
241
|
+
identifier: null,
|
|
242
|
+
action: "continue",
|
|
243
|
+
confidence: "medium",
|
|
244
|
+
ambiguityReason: "'continue'/'resume' matched without an explicit task id or resolved project; could be a task or a project — confirm before retrieval",
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
if (matchesAny(text, EXECUTE)) {
|
|
248
|
+
return {
|
|
249
|
+
intent: "execute",
|
|
250
|
+
entityType: "execution",
|
|
251
|
+
identifier: null,
|
|
252
|
+
action: "execute",
|
|
253
|
+
confidence: "high",
|
|
254
|
+
ambiguityReason: null,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return safeResult("no recognizable entity, verb, or identifier matched in the request");
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=intent-router.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { oceanEnvPair } from "@ocean/core/shared/paths.js";
|
|
2
|
+
// This module used to read Ocean files (memory, knowledge, inbox, README, governance rules)
|
|
3
|
+
// and inject their full content into every provider launch. That bulk injection is removed
|
|
4
|
+
// per T-198: providers get a tiny identity/bootstrap signal only, never Ocean file contents.
|
|
5
|
+
// On-demand reads happen through explicit Ocean operations (tasks, memory, knowledge, …),
|
|
6
|
+
// not through what gets stuffed into a launch argument or env var at startup.
|
|
7
|
+
export const OCEAN_BOOTSTRAP_MAX_BYTES = 256;
|
|
8
|
+
const SUPPORTED_OPERATIONS = "context,tasks,memory-search,knowledge-search";
|
|
9
|
+
function projectTag(project) {
|
|
10
|
+
if (project.status === "bound")
|
|
11
|
+
return project.projectId;
|
|
12
|
+
return project.status;
|
|
13
|
+
}
|
|
14
|
+
// Bounded, client-neutral: identifies Ocean as canonical and the resolved project (or its
|
|
15
|
+
// absence), and names the on-demand operations a client can call. Never Ocean file content.
|
|
16
|
+
export function buildOceanBootstrap(project, extra = {}) {
|
|
17
|
+
const rules = extra.rules && extra.rules > 0 ? ` rules=${extra.rules}` : "";
|
|
18
|
+
const content = `ocean=1 project=${projectTag(project)} confidence=${project.confidence}${rules} ops=${SUPPORTED_OPERATIONS}`;
|
|
19
|
+
const bytes = Buffer.byteLength(content, "utf8");
|
|
20
|
+
if (bytes > OCEAN_BOOTSTRAP_MAX_BYTES) {
|
|
21
|
+
throw new Error(`Ocean bootstrap exceeds the ${OCEAN_BOOTSTRAP_MAX_BYTES}-byte budget (${bytes} bytes): ${content}`);
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
content,
|
|
25
|
+
manifest: { bytes, source: "ocean", transport: "bootstrap-env" },
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// Delivered as environment variables only — never written into provider argv or a
|
|
29
|
+
// provider-owned context file, and never a second source of truth for provider memory.
|
|
30
|
+
export function bootstrapEnvironment(bootstrap) {
|
|
31
|
+
return {
|
|
32
|
+
...oceanEnvPair("BOOTSTRAP", bootstrap.content),
|
|
33
|
+
...oceanEnvPair("BOOTSTRAP_BYTES", String(bootstrap.manifest.bytes)),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=resource-injection.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
// Canonical brain-record schema (T-228): the frontmatter shape every markdown record under
|
|
3
|
+
// brain/02-personal and brain/05-knowledge normalizes into. Types + zod only, no I/O — the
|
|
4
|
+
// markdown layer (application/brain/brain-markdown.ts) is what actually reads legacy
|
|
5
|
+
// frontmatter and maps it onto this shape.
|
|
6
|
+
export const brainRecordSchema = z.object({
|
|
7
|
+
id: z.string().min(1),
|
|
8
|
+
title: z.string().min(1),
|
|
9
|
+
summary: z.string().default(""),
|
|
10
|
+
tags: z.array(z.string()).default([]),
|
|
11
|
+
type: z.string().default("unknown"),
|
|
12
|
+
confidence: z.string().default("unknown"),
|
|
13
|
+
created: z.string().nullable().default(null),
|
|
14
|
+
updated: z.string().nullable().default(null),
|
|
15
|
+
lastConfirmedAt: z.string().nullable().default(null),
|
|
16
|
+
// Normalized by domain/brain/record-scope.ts: "global", "project:<id>" or "invalid".
|
|
17
|
+
scope: z.string().default("global"),
|
|
18
|
+
});
|
|
19
|
+
export function validateBrainRecord(input) {
|
|
20
|
+
return brainRecordSchema.parse(input);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=brain-record.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isValidProjectId } from "../context/project-id.js";
|
|
2
|
+
export function normalizeScope(raw) {
|
|
3
|
+
const value = (raw ?? "").trim();
|
|
4
|
+
if (value === "")
|
|
5
|
+
return { scope: "global", state: "defaulted" };
|
|
6
|
+
if (value === "global")
|
|
7
|
+
return { scope: "global", state: "declared" };
|
|
8
|
+
if (value.startsWith("project:")) {
|
|
9
|
+
const id = value.slice("project:".length);
|
|
10
|
+
if (isValidProjectId(id))
|
|
11
|
+
return { scope: `project:${id}`, state: "declared" };
|
|
12
|
+
}
|
|
13
|
+
return { scope: "invalid", state: "invalid" };
|
|
14
|
+
}
|
|
15
|
+
// Bound to P → global + project:P; unbound or ambiguous (null) → global only.
|
|
16
|
+
export function visibleScopes(project) {
|
|
17
|
+
return project ? ["global", `project:${project}`] : ["global"];
|
|
18
|
+
}
|
|
19
|
+
export function scopeVisible(scope, project) {
|
|
20
|
+
return visibleScopes(project).includes(scope);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=record-scope.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { capabilityContractSchema, } from "./capability-contract.js";
|
|
3
|
+
const browserOperationSchema = z.enum([
|
|
4
|
+
"open",
|
|
5
|
+
"close",
|
|
6
|
+
"approve",
|
|
7
|
+
"navigate",
|
|
8
|
+
"read",
|
|
9
|
+
"observe",
|
|
10
|
+
"extract",
|
|
11
|
+
"click",
|
|
12
|
+
"type",
|
|
13
|
+
"replace-text",
|
|
14
|
+
"select",
|
|
15
|
+
"scroll",
|
|
16
|
+
"wait",
|
|
17
|
+
"upload",
|
|
18
|
+
"download",
|
|
19
|
+
"submit",
|
|
20
|
+
]);
|
|
21
|
+
// "required" always needs owner approval before execution; "conditional" needs it only
|
|
22
|
+
// when the application layer determines the specific call crosses a trust boundary
|
|
23
|
+
// (navigate: only when the destination origin differs from the session's current origin).
|
|
24
|
+
const browserApprovalSchema = z.enum(["none", "required", "conditional"]);
|
|
25
|
+
const browserCapabilityContractSchema = capabilityContractSchema.extend({
|
|
26
|
+
capability: z.literal("browser"),
|
|
27
|
+
operation: browserOperationSchema,
|
|
28
|
+
approval: browserApprovalSchema,
|
|
29
|
+
});
|
|
30
|
+
function define(operation, authority, idempotency, approval, verification) {
|
|
31
|
+
return browserCapabilityContractSchema.parse({
|
|
32
|
+
capability: "browser",
|
|
33
|
+
operation,
|
|
34
|
+
authority,
|
|
35
|
+
idempotency,
|
|
36
|
+
approval,
|
|
37
|
+
verification,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// authority/idempotency/approval mirror the retired Python prototype's per-operation
|
|
41
|
+
// authority (observe/execute/execute-with-approval) and idempotent flag, translated onto
|
|
42
|
+
// this codebase's authority scopes (profile/session/owner) plus an orthogonal approval
|
|
43
|
+
// gate. Every read-only operation stays session-scoped and safe; state-changing
|
|
44
|
+
// operations are session-scoped and non-repeatable; upload/download/submit and
|
|
45
|
+
// cross-origin navigation require owner approval.
|
|
46
|
+
export const browserContracts = {
|
|
47
|
+
open: define("open", "session", "non-repeatable", "none", "session record reaches status running with a live CDP endpoint"),
|
|
48
|
+
close: define("close", "session", "repeatable", "none", "session record reaches a terminal status and the browser process has exited"),
|
|
49
|
+
approve: define("approve", "owner", "repeatable", "required", "session approval is persisted and expires when the browser session closes"),
|
|
50
|
+
navigate: define("navigate", "session", "repeatable", "conditional", "live page URL matches the requested destination"),
|
|
51
|
+
read: define("read", "session", "safe", "none", "returned text is re-read from the live page, not cached"),
|
|
52
|
+
observe: define("observe", "session", "safe", "none", "returned elements are re-read from the live page, not cached"),
|
|
53
|
+
extract: define("extract", "session", "safe", "none", "returned values are re-read from the live page via the given selector"),
|
|
54
|
+
click: define("click", "session", "non-repeatable", "none", "the caller's expected outcome (navigation, staying on the page, or a selector's live element count) is re-checked against live state after the click"),
|
|
55
|
+
type: define("type", "session", "non-repeatable", "none", "live input value is re-read from the target element after typing"),
|
|
56
|
+
"replace-text": define("replace-text", "session", "non-repeatable", "none", "the live editor value contains the requested replacement and the selected occurrence was unique or explicit"),
|
|
57
|
+
select: define("select", "session", "non-repeatable", "none", "live input value is re-read from the target element after selecting"),
|
|
58
|
+
scroll: define("scroll", "session", "repeatable", "none", "live scroll position is re-read after scrolling"),
|
|
59
|
+
wait: define("wait", "session", "safe", "none", "the awaited selector or URL pattern is confirmed present on the live page"),
|
|
60
|
+
upload: define("upload", "owner", "non-repeatable", "required", "live file input reports the attached file names"),
|
|
61
|
+
download: define("download", "owner", "non-repeatable", "required", "downloaded file exists on disk with a non-zero size at the reported path"),
|
|
62
|
+
submit: define("submit", "owner", "non-repeatable", "required", "live page URL and load state are re-read after submission"),
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=browser-contract.js.map
|