@amoree-code/ocean 0.0.0-stage → 1.2.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/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/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-hub.js +220 -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,84 @@
|
|
|
1
|
+
import { redactRuntimeText } from "@ocean/core/domain/redaction/redaction.js";
|
|
2
|
+
const CORRECTION_PATTERN = /\bno,|\bactually,|\bnot that\b|\binstead of\b|\bshould use\b|\bthat'?s wrong\b/i;
|
|
3
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: matching terminal escape sequences
|
|
4
|
+
const ANSI_PATTERN = /\u001b\[[0-?]*[ -/]*[@-~]/g;
|
|
5
|
+
// Runtime chatter (hook_response JSON, terminal redraws) repeats by construction, not by lesson.
|
|
6
|
+
function isRuntimeNoise(text) {
|
|
7
|
+
if (text.length <= 12)
|
|
8
|
+
return true;
|
|
9
|
+
if (!text.startsWith("{"))
|
|
10
|
+
return false;
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(text)?.type === "system";
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function userInputTexts(events) {
|
|
19
|
+
return events
|
|
20
|
+
.filter((event) => event.type === "user_input")
|
|
21
|
+
.map((event) => ({
|
|
22
|
+
id: event.eventId,
|
|
23
|
+
text: redactRuntimeText(event.data).replace(/\s+/g, " ").trim(),
|
|
24
|
+
}))
|
|
25
|
+
.filter((event) => event.text.length > 12)
|
|
26
|
+
.slice(0, 80);
|
|
27
|
+
}
|
|
28
|
+
// True when the user pushed back in this session ("no, use X", "that's wrong").
|
|
29
|
+
export function hasUserCorrection(events) {
|
|
30
|
+
return userInputTexts(events).some((event) => CORRECTION_PATTERN.test(event.text));
|
|
31
|
+
}
|
|
32
|
+
// What a completed, proven session is worth remembering. Pure: reads events, writes nothing.
|
|
33
|
+
export function detectSessionSignals(events) {
|
|
34
|
+
const proven = events.filter((event) => event.type === "evidence" && /"result"\s*:\s*"proven"/.test(event.data));
|
|
35
|
+
if (!proven.length)
|
|
36
|
+
return [];
|
|
37
|
+
const outputs = events
|
|
38
|
+
.filter((event) => ["provider_output", "text", "json"].includes(event.type))
|
|
39
|
+
.map((event) => ({
|
|
40
|
+
id: event.eventId,
|
|
41
|
+
text: redactRuntimeText(event.data)
|
|
42
|
+
.replace(ANSI_PATTERN, "")
|
|
43
|
+
.replace(/\s+/g, " ")
|
|
44
|
+
.trim(),
|
|
45
|
+
}))
|
|
46
|
+
.filter((event) => !isRuntimeNoise(event.text))
|
|
47
|
+
.slice(0, 80);
|
|
48
|
+
const userInputs = userInputTexts(events);
|
|
49
|
+
const candidates = [];
|
|
50
|
+
const decisions = outputs.filter((event) => /\bdecision\s*:/i.test(event.text));
|
|
51
|
+
if (decisions.length)
|
|
52
|
+
candidates.push({
|
|
53
|
+
signalType: "explicit-decision",
|
|
54
|
+
summary: decisions[0].text.slice(0, 500),
|
|
55
|
+
refs: decisions.slice(0, 4).map((event) => `event:${event.id}`),
|
|
56
|
+
confidence: 0.9,
|
|
57
|
+
});
|
|
58
|
+
const counts = new Map();
|
|
59
|
+
for (const event of outputs) {
|
|
60
|
+
const normalized = event.text.toLocaleLowerCase();
|
|
61
|
+
const current = counts.get(normalized) ?? { count: 0, refs: [] };
|
|
62
|
+
current.count += 1;
|
|
63
|
+
current.refs.push(`event:${event.id}`);
|
|
64
|
+
counts.set(normalized, current);
|
|
65
|
+
}
|
|
66
|
+
const repeated = [...counts.entries()].find(([, value]) => value.count >= 2);
|
|
67
|
+
if (repeated)
|
|
68
|
+
candidates.push({
|
|
69
|
+
signalType: "repeated-procedure",
|
|
70
|
+
summary: repeated[0].slice(0, 500),
|
|
71
|
+
refs: repeated[1].refs.slice(0, 4),
|
|
72
|
+
confidence: 0.8,
|
|
73
|
+
});
|
|
74
|
+
const corrections = userInputs.filter((event) => CORRECTION_PATTERN.test(event.text));
|
|
75
|
+
if (corrections.length)
|
|
76
|
+
candidates.push({
|
|
77
|
+
signalType: "repeated-correction",
|
|
78
|
+
summary: corrections[0].text.slice(0, 500),
|
|
79
|
+
refs: corrections.slice(0, 4).map((event) => `event:${event.id}`),
|
|
80
|
+
confidence: 0.7,
|
|
81
|
+
});
|
|
82
|
+
return candidates;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=signal-detection.js.map
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { cp, lstat, mkdir, readdir, readFile, readlink, rename, rm, stat, symlink, } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { enginePath, oceanPath, SYSTEM_DIR } from "@ocean/core/shared/paths.js";
|
|
6
|
+
import { clientSkillRoots } from "./core-skill-sync.js";
|
|
7
|
+
// The hub is the one place skills live inside Ocean. Clients hold symlinks into it, so
|
|
8
|
+
// there is a single copy to edit and nothing drifts per client.
|
|
9
|
+
export function skillHubRoot() {
|
|
10
|
+
return oceanPath(SYSTEM_DIR, "skills");
|
|
11
|
+
}
|
|
12
|
+
async function skillDirectories(root) {
|
|
13
|
+
const names = [];
|
|
14
|
+
for (const entry of await readdir(root, { withFileTypes: true })) {
|
|
15
|
+
// Follow symlinks: a client may expose another store's skills as links.
|
|
16
|
+
const full = path.join(root, entry.name);
|
|
17
|
+
const isDirectory = entry.isDirectory() || (await stat(full)).isDirectory();
|
|
18
|
+
if (!isDirectory)
|
|
19
|
+
continue;
|
|
20
|
+
try {
|
|
21
|
+
await stat(path.join(full, "SKILL.md"));
|
|
22
|
+
names.push(entry.name);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
/* not a skill directory */
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return names.sort();
|
|
29
|
+
}
|
|
30
|
+
// Additive: copies the engine's core skills and any extra source folders into the hub.
|
|
31
|
+
// An existing hub skill is never overwritten.
|
|
32
|
+
export async function populateSkillHub(extraSources = []) {
|
|
33
|
+
const hub = skillHubRoot();
|
|
34
|
+
await mkdir(hub, { recursive: true });
|
|
35
|
+
const added = [];
|
|
36
|
+
const kept = [];
|
|
37
|
+
for (const source of [enginePath("skills", "core"), ...extraSources]) {
|
|
38
|
+
for (const name of await skillDirectories(source)) {
|
|
39
|
+
const target = path.join(hub, name);
|
|
40
|
+
try {
|
|
41
|
+
await lstat(target);
|
|
42
|
+
kept.push(name);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
await cp(path.join(source, name), target, {
|
|
46
|
+
recursive: true,
|
|
47
|
+
dereference: true,
|
|
48
|
+
});
|
|
49
|
+
added.push(name);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { added, kept };
|
|
54
|
+
}
|
|
55
|
+
// Dry run unless `apply`. Skills a client does not already have are only added with `all`:
|
|
56
|
+
// every extra skill costs that client always-on context. A real directory (or a link elsewhere) already at the target is
|
|
57
|
+
// moved into a timestamped backup under the bridge archive before the link is made.
|
|
58
|
+
export async function linkClientSkills(options) {
|
|
59
|
+
const home = options.home ?? os.homedir();
|
|
60
|
+
const hub = options.hub ?? skillHubRoot();
|
|
61
|
+
const backup = path.join(oceanPath(SYSTEM_DIR, "archive"), `skills-backup-${new Date().toISOString().replace(/[:.]/g, "-")}`);
|
|
62
|
+
const actions = [];
|
|
63
|
+
let backedUp = false;
|
|
64
|
+
const names = await skillDirectories(hub);
|
|
65
|
+
for (const client of Object.keys(clientSkillRoots)) {
|
|
66
|
+
const root = clientSkillRoots[client](home);
|
|
67
|
+
try {
|
|
68
|
+
await stat(root);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
continue; // client not installed
|
|
72
|
+
}
|
|
73
|
+
for (const skill of names) {
|
|
74
|
+
const target = path.join(root, skill);
|
|
75
|
+
const wanted = path.join(hub, skill);
|
|
76
|
+
let action = "create";
|
|
77
|
+
try {
|
|
78
|
+
const info = await lstat(target);
|
|
79
|
+
if (info.isSymbolicLink() &&
|
|
80
|
+
path.resolve(root, await readlink(target)) === wanted) {
|
|
81
|
+
action = "linked";
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
action = "replace";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
/* absent */
|
|
89
|
+
}
|
|
90
|
+
if (action === "create" && !options.all)
|
|
91
|
+
action = "skip";
|
|
92
|
+
actions.push({ client, skill, action });
|
|
93
|
+
if (!options.apply || action === "linked" || action === "skip")
|
|
94
|
+
continue;
|
|
95
|
+
if (action === "replace") {
|
|
96
|
+
const saved = path.join(backup, client);
|
|
97
|
+
await mkdir(saved, { recursive: true });
|
|
98
|
+
await rename(target, path.join(saved, skill));
|
|
99
|
+
backedUp = true;
|
|
100
|
+
}
|
|
101
|
+
await symlink(wanted, target, "dir");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return { actions, backup: backedUp ? backup : null };
|
|
105
|
+
}
|
|
106
|
+
// Clients that get the hub's skills as copies. Hermes manages its own bundle and curator,
|
|
107
|
+
// so it is left alone unless asked for by name.
|
|
108
|
+
export const COPY_CLIENTS = [
|
|
109
|
+
"claude",
|
|
110
|
+
"codex",
|
|
111
|
+
"gemini",
|
|
112
|
+
"hermes",
|
|
113
|
+
];
|
|
114
|
+
// Content hash of a whole skill directory: paths and bytes, so a changed script or reference
|
|
115
|
+
// counts as drift, not only SKILL.md.
|
|
116
|
+
// A symlink inside a skill is refused: copying it would either dereference into data outside
|
|
117
|
+
// the hub or leave a dangling link in the client, and a loop would never finish.
|
|
118
|
+
async function directoryHash(directory) {
|
|
119
|
+
const hash = createHash("sha256");
|
|
120
|
+
async function walk(current, prefix) {
|
|
121
|
+
const entries = (await readdir(current, { withFileTypes: true })).sort((left, right) => left.name.localeCompare(right.name));
|
|
122
|
+
for (const entry of entries) {
|
|
123
|
+
const full = path.join(current, entry.name);
|
|
124
|
+
const relative = path.posix.join(prefix, entry.name);
|
|
125
|
+
const info = await lstat(full);
|
|
126
|
+
if (info.isSymbolicLink())
|
|
127
|
+
throw new Error(`${relative} is a symlink inside a skill`);
|
|
128
|
+
if (info.isDirectory())
|
|
129
|
+
await walk(full, relative);
|
|
130
|
+
else
|
|
131
|
+
hash.update(relative).update(await readFile(full));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
await walk(directory, "");
|
|
135
|
+
return hash.digest("hex");
|
|
136
|
+
}
|
|
137
|
+
// Gives every client a real copy of every hub skill, replacing the symlink farm. Dry run
|
|
138
|
+
// unless `apply`. A symlink is only unlinked (its target is untouched); a real directory
|
|
139
|
+
// that differs from the hub is moved into a timestamped backup, never deleted. Anything in a
|
|
140
|
+
// client that the hub does not name is left alone.
|
|
141
|
+
export async function copyClientSkills(options) {
|
|
142
|
+
const home = options.home ?? os.homedir();
|
|
143
|
+
const hub = options.hub ?? skillHubRoot();
|
|
144
|
+
const backup = path.join(options.archive ?? oceanPath(SYSTEM_DIR, "archive"), `skills-backup-${new Date().toISOString().replace(/[:.]/g, "-")}`);
|
|
145
|
+
const actions = [];
|
|
146
|
+
let backedUp = false;
|
|
147
|
+
const names = await skillDirectories(hub);
|
|
148
|
+
for (const client of options.clients ?? COPY_CLIENTS) {
|
|
149
|
+
const root = clientSkillRoots[client](home);
|
|
150
|
+
try {
|
|
151
|
+
await stat(root);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
continue; // client not installed
|
|
155
|
+
}
|
|
156
|
+
for (const skill of names) {
|
|
157
|
+
const target = path.join(root, skill);
|
|
158
|
+
const source = path.join(hub, skill);
|
|
159
|
+
try {
|
|
160
|
+
let action = "create";
|
|
161
|
+
let info = null;
|
|
162
|
+
try {
|
|
163
|
+
info = await lstat(target);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
/* absent */
|
|
167
|
+
}
|
|
168
|
+
const wanted = await directoryHash(source);
|
|
169
|
+
let ours = false;
|
|
170
|
+
if (info?.isSymbolicLink()) {
|
|
171
|
+
action = "unlink";
|
|
172
|
+
// A link into the hub holds nothing of its own. Any other link is moved into the
|
|
173
|
+
// backup (the link itself, intact) so what it pointed at stays reachable.
|
|
174
|
+
ours = path
|
|
175
|
+
.resolve(root, await readlink(target))
|
|
176
|
+
.startsWith(hub + path.sep);
|
|
177
|
+
}
|
|
178
|
+
else if (info) {
|
|
179
|
+
action =
|
|
180
|
+
info.isDirectory() && (await directoryHash(target)) === wanted
|
|
181
|
+
? "synchronized"
|
|
182
|
+
: "replace";
|
|
183
|
+
}
|
|
184
|
+
actions.push({ client, skill, action });
|
|
185
|
+
if (!options.apply || action === "synchronized")
|
|
186
|
+
continue;
|
|
187
|
+
if (action === "replace" || (action === "unlink" && !ours)) {
|
|
188
|
+
const saved = path.join(backup, client);
|
|
189
|
+
await mkdir(saved, { recursive: true });
|
|
190
|
+
await rename(target, path.join(saved, skill));
|
|
191
|
+
backedUp = true;
|
|
192
|
+
}
|
|
193
|
+
else if (action === "unlink")
|
|
194
|
+
await rm(target);
|
|
195
|
+
// Copy beside the target and rename, so a failed copy never leaves a half skill.
|
|
196
|
+
const staging = path.join(root, `.${skill}.copying`);
|
|
197
|
+
await rm(staging, { recursive: true, force: true });
|
|
198
|
+
try {
|
|
199
|
+
await cp(source, staging, { recursive: true });
|
|
200
|
+
await rename(staging, target);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
await rm(staging, { recursive: true, force: true });
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
// One bad skill must not abort the run halfway through every client.
|
|
209
|
+
actions.push({
|
|
210
|
+
client,
|
|
211
|
+
skill,
|
|
212
|
+
action: "failed",
|
|
213
|
+
error: error instanceof Error ? error.message : String(error),
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return { actions, backup: backedUp ? backup : null };
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=skill-hub.js.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Enforces the browser capability's authority/approval policy and drives live
|
|
2
|
+
// verification. This is where operation gating lives — providers only execute what
|
|
3
|
+
// they are asked and report what happened; they never decide whether an action was
|
|
4
|
+
// approved or verified.
|
|
5
|
+
import { browserContracts, } from "@ocean/core/domain/capabilities/browser-contract.js";
|
|
6
|
+
export class BrowserApprovalRequiredError extends Error {
|
|
7
|
+
constructor(operation, reason) {
|
|
8
|
+
super(`Browser operation '${operation}' requires owner approval: ${reason}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function isExternalOrigin(currentUrl, targetUrl) {
|
|
12
|
+
if (currentUrl === "about:blank")
|
|
13
|
+
return false;
|
|
14
|
+
try {
|
|
15
|
+
return new URL(currentUrl).origin !== new URL(targetUrl).origin;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class BrowserService {
|
|
22
|
+
provider;
|
|
23
|
+
constructor(provider) {
|
|
24
|
+
this.provider = provider;
|
|
25
|
+
}
|
|
26
|
+
detect() {
|
|
27
|
+
return this.provider.detect();
|
|
28
|
+
}
|
|
29
|
+
launch(profileDir, port) {
|
|
30
|
+
return this.provider.launch(profileDir, port);
|
|
31
|
+
}
|
|
32
|
+
connect(launch) {
|
|
33
|
+
return this.provider.connect(launch);
|
|
34
|
+
}
|
|
35
|
+
close(launch) {
|
|
36
|
+
return this.provider.close(launch);
|
|
37
|
+
}
|
|
38
|
+
requireApproval(operation, approved, reason) {
|
|
39
|
+
const contract = browserContracts[operation];
|
|
40
|
+
if (contract.approval === "required" && !approved)
|
|
41
|
+
throw new BrowserApprovalRequiredError(operation, reason);
|
|
42
|
+
}
|
|
43
|
+
async navigate(handle, url, options = {}) {
|
|
44
|
+
const before = await handle.state();
|
|
45
|
+
const crossOrigin = isExternalOrigin(before.url, url);
|
|
46
|
+
if (crossOrigin && !options.approved)
|
|
47
|
+
throw new BrowserApprovalRequiredError("navigate", `destination origin differs from current origin (${before.url} -> ${url})`);
|
|
48
|
+
const result = await handle.navigate(url, options.timeoutMs);
|
|
49
|
+
const verified = result.url === url || result.url.startsWith(url);
|
|
50
|
+
return {
|
|
51
|
+
operation: "navigate",
|
|
52
|
+
approved: crossOrigin ? Boolean(options.approved) : true,
|
|
53
|
+
verified,
|
|
54
|
+
result,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async upload(handle, selector, paths, options = {}) {
|
|
58
|
+
this.requireApproval("upload", Boolean(options.approved), "attaching local files to a page is irreversible from Ocean's point of view");
|
|
59
|
+
const result = await handle.upload(selector, paths);
|
|
60
|
+
return {
|
|
61
|
+
operation: "upload",
|
|
62
|
+
approved: true,
|
|
63
|
+
verified: result.attached.length === paths.length,
|
|
64
|
+
result,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async download(handle, selector, destinationDir, options = {}) {
|
|
68
|
+
this.requireApproval("download", Boolean(options.approved), "saving a file to disk is irreversible from Ocean's point of view");
|
|
69
|
+
const result = await handle.download(selector, destinationDir, options.timeoutMs);
|
|
70
|
+
return {
|
|
71
|
+
operation: "download",
|
|
72
|
+
approved: true,
|
|
73
|
+
verified: result.size > 0,
|
|
74
|
+
result,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async submit(handle, selector, options = {}) {
|
|
78
|
+
this.requireApproval("submit", Boolean(options.approved), "submitting a form is irreversible from Ocean's point of view");
|
|
79
|
+
const result = await handle.submit(selector, options.timeoutMs);
|
|
80
|
+
const verified = result.url !== result.urlBefore || result.bodyAfter !== result.bodyBefore;
|
|
81
|
+
return { operation: "submit", approved: true, verified, result };
|
|
82
|
+
}
|
|
83
|
+
async click(handle, selector, expectation, timeoutMs) {
|
|
84
|
+
const result = await handle.click(selector, timeoutMs);
|
|
85
|
+
let verified;
|
|
86
|
+
if (expectation.kind === "navigates")
|
|
87
|
+
verified = result.url !== result.urlBefore;
|
|
88
|
+
else if (expectation.kind === "stays")
|
|
89
|
+
verified = result.url === result.urlBefore;
|
|
90
|
+
else
|
|
91
|
+
verified =
|
|
92
|
+
(await handle.extract(expectation.selector, null)).count ===
|
|
93
|
+
expectation.expected;
|
|
94
|
+
return { operation: "click", approved: true, verified, result };
|
|
95
|
+
}
|
|
96
|
+
async type(handle, selector, text, clear) {
|
|
97
|
+
const result = await handle.type(selector, text, clear);
|
|
98
|
+
return {
|
|
99
|
+
operation: "type",
|
|
100
|
+
approved: true,
|
|
101
|
+
verified: result.value.includes(text) || !clear,
|
|
102
|
+
result,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async select(handle, selector, value) {
|
|
106
|
+
const result = await handle.select(selector, value);
|
|
107
|
+
return {
|
|
108
|
+
operation: "select",
|
|
109
|
+
approved: true,
|
|
110
|
+
verified: result.value === value,
|
|
111
|
+
result,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async replaceText(handle, selector, oldText, newText, occurrence = 1) {
|
|
115
|
+
const result = await handle.replaceText(selector, oldText, newText, occurrence);
|
|
116
|
+
return {
|
|
117
|
+
operation: "replace-text",
|
|
118
|
+
approved: true,
|
|
119
|
+
verified: result.value.includes(newText),
|
|
120
|
+
result,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=browser-service.js.map
|