@milpa/comal 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -28
- package/backend/contracts/remote-commands-v1.json +125 -0
- package/bin/comal.mjs +16 -5
- package/docs/investigation.md +2 -2
- package/governance/chrome-task-v2.json +67 -0
- package/node_modules/@jitl/quickjs-ffi-types/LICENSE +21 -0
- package/node_modules/@jitl/quickjs-ffi-types/README.md +5 -0
- package/node_modules/@jitl/quickjs-ffi-types/dist/index.d.mts +549 -0
- package/node_modules/@jitl/quickjs-ffi-types/dist/index.d.ts +549 -0
- package/node_modules/@jitl/quickjs-ffi-types/dist/index.js +2 -0
- package/node_modules/@jitl/quickjs-ffi-types/dist/index.js.map +1 -0
- package/node_modules/@jitl/quickjs-ffi-types/dist/index.mjs +2 -0
- package/node_modules/@jitl/quickjs-ffi-types/dist/index.mjs.map +1 -0
- package/node_modules/@jitl/quickjs-ffi-types/package.json +36 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/LICENSE +47 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/README.md +93 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/emscripten-module.browser.d.ts +15 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/emscripten-module.browser.mjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/emscripten-module.cjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/emscripten-module.cloudflare.cjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/emscripten-module.cloudflare.d.ts +15 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/emscripten-module.d.ts +15 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/emscripten-module.mjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/emscripten-module.wasm +0 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/emscripten-module.wasm.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/ffi.d.mts +94 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/ffi.d.ts +94 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/ffi.js +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/ffi.js.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/ffi.mjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/ffi.mjs.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/index.d.mts +20 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/index.d.ts +20 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/index.js +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/index.js.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/index.mjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/index.mjs.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/package.json +61 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/LICENSE +47 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/README.md +88 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/emscripten-module.browser.d.ts +11 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/emscripten-module.browser.mjs +5430 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/emscripten-module.cjs +5485 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/emscripten-module.cloudflare.cjs +5424 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/emscripten-module.cloudflare.d.ts +11 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/emscripten-module.d.ts +11 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/emscripten-module.mjs +5541 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/emscripten-module.wasm +0 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/emscripten-module.wasm.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/ffi.d.mts +85 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/ffi.d.ts +85 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/ffi.js +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/ffi.js.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/ffi.mjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/ffi.mjs.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/index.d.mts +20 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/index.d.ts +20 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/index.js +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/index.js.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/index.mjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/index.mjs.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-debug-sync/package.json +61 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/LICENSE +47 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/README.md +89 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/emscripten-module.browser.d.ts +15 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/emscripten-module.browser.mjs +26 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/emscripten-module.cjs +25 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/emscripten-module.cloudflare.cjs +25 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/emscripten-module.cloudflare.d.ts +15 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/emscripten-module.d.ts +15 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/emscripten-module.mjs +30 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/emscripten-module.wasm +0 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/ffi.d.mts +94 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/ffi.d.ts +94 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/ffi.js +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/ffi.js.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/ffi.mjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/ffi.mjs.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/index.d.mts +20 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/index.d.ts +20 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/index.js +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/index.js.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/index.mjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/index.mjs.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-asyncify/package.json +61 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/LICENSE +47 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/README.md +82 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.browser.d.ts +11 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.browser.mjs +22 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.cjs +21 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.cloudflare.cjs +21 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.cloudflare.d.ts +11 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.d.ts +11 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.mjs +25 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.wasm +0 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/ffi.d.mts +85 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/ffi.d.ts +85 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/ffi.js +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/ffi.js.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/ffi.mjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/ffi.mjs.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/index.d.mts +20 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/index.d.ts +20 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/index.js +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/index.js.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/index.mjs +2 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/index.mjs.map +1 -0
- package/node_modules/@jitl/quickjs-wasmfile-release-sync/package.json +61 -0
- package/node_modules/quickjs-emscripten/LICENSE +21 -0
- package/node_modules/quickjs-emscripten/README.md +928 -0
- package/node_modules/quickjs-emscripten/dist/chunk-OHAYRCBA.mjs +2 -0
- package/node_modules/quickjs-emscripten/dist/chunk-OHAYRCBA.mjs.map +1 -0
- package/node_modules/quickjs-emscripten/dist/index.d.mts +57 -0
- package/node_modules/quickjs-emscripten/dist/index.d.ts +57 -0
- package/node_modules/quickjs-emscripten/dist/index.global.js +7309 -0
- package/node_modules/quickjs-emscripten/dist/index.js +2 -0
- package/node_modules/quickjs-emscripten/dist/index.js.map +1 -0
- package/node_modules/quickjs-emscripten/dist/index.mjs +2 -0
- package/node_modules/quickjs-emscripten/dist/index.mjs.map +1 -0
- package/node_modules/quickjs-emscripten/dist/variants.d.mts +39 -0
- package/node_modules/quickjs-emscripten/dist/variants.d.ts +39 -0
- package/node_modules/quickjs-emscripten/dist/variants.js +2 -0
- package/node_modules/quickjs-emscripten/dist/variants.js.map +1 -0
- package/node_modules/quickjs-emscripten/dist/variants.mjs +2 -0
- package/node_modules/quickjs-emscripten/dist/variants.mjs.map +1 -0
- package/node_modules/quickjs-emscripten/package.json +69 -0
- package/node_modules/quickjs-emscripten-core/LICENSE +21 -0
- package/node_modules/quickjs-emscripten-core/README.md +341 -0
- package/node_modules/quickjs-emscripten-core/README.template.md +70 -0
- package/node_modules/quickjs-emscripten-core/dist/chunk-TAV5CUKK.mjs +2 -0
- package/node_modules/quickjs-emscripten-core/dist/chunk-TAV5CUKK.mjs.map +1 -0
- package/node_modules/quickjs-emscripten-core/dist/chunk-V2S4ZYJR.mjs +7 -0
- package/node_modules/quickjs-emscripten-core/dist/chunk-V2S4ZYJR.mjs.map +1 -0
- package/node_modules/quickjs-emscripten-core/dist/index.d.mts +2033 -0
- package/node_modules/quickjs-emscripten-core/dist/index.d.ts +2033 -0
- package/node_modules/quickjs-emscripten-core/dist/index.js +7 -0
- package/node_modules/quickjs-emscripten-core/dist/index.js.map +1 -0
- package/node_modules/quickjs-emscripten-core/dist/index.mjs +2 -0
- package/node_modules/quickjs-emscripten-core/dist/index.mjs.map +1 -0
- package/node_modules/quickjs-emscripten-core/dist/module-ES6BEMUI.mjs +2 -0
- package/node_modules/quickjs-emscripten-core/dist/module-ES6BEMUI.mjs.map +1 -0
- package/node_modules/quickjs-emscripten-core/dist/module-asyncify-2EFITU5U.mjs +2 -0
- package/node_modules/quickjs-emscripten-core/dist/module-asyncify-2EFITU5U.mjs.map +1 -0
- package/node_modules/quickjs-emscripten-core/package.json +49 -0
- package/package.json +6 -2
- package/src/agent.mjs +17 -6
- package/src/artifact-tools.mjs +3 -2
- package/src/auto-guard.mjs +5 -1
- package/src/cli.mjs +267 -201
- package/src/cloud-client.mjs +58 -8
- package/src/code-run-worker.mjs +70 -0
- package/src/code-run.mjs +97 -0
- package/src/codex-provider.mjs +28 -28
- package/src/compaction.mjs +1 -1
- package/src/goals.mjs +2 -2
- package/src/house.mjs +24 -24
- package/src/i18n/cli-es.mjs +97 -0
- package/src/i18n/es.mjs +227 -0
- package/src/i18n/index.mjs +48 -0
- package/src/image-attachments.mjs +263 -0
- package/src/model-controls.mjs +4 -4
- package/src/onboarding.mjs +91 -0
- package/src/openai-provider.mjs +24 -24
- package/src/peer-tools.mjs +4 -3
- package/src/permissions.mjs +53 -17
- package/src/provider-auth.mjs +30 -30
- package/src/provider-profiles.mjs +27 -27
- package/src/provider.mjs +30 -30
- package/src/remote-commands.mjs +11 -0
- package/src/remote-control.mjs +180 -0
- package/src/sessions.mjs +3 -3
- package/src/subagents.mjs +7 -3
- package/src/tool-packs.mjs +5 -2
- package/src/tools.mjs +92 -34
- package/src/tui/brand.mjs +2 -1
- package/src/tui/clipboard.mjs +5 -4
- package/src/tui/composer.mjs +77 -11
- package/src/tui/controller.mjs +111 -29
- package/src/tui/discovery.mjs +105 -100
- package/src/tui/markdown.mjs +10 -9
- package/src/tui/message-queue.mjs +27 -9
- package/src/tui/screen.mjs +209 -118
- package/src/vision.mjs +31 -3
- package/src/work-assertions.mjs +7 -7
- package/src/work-plan.mjs +189 -21
- package/src/work-recovery-proof.mjs +13 -1
- package/src/workflows/chrome-task.mjs +24 -7
package/src/house.mjs
CHANGED
|
@@ -45,13 +45,13 @@ export class House {
|
|
|
45
45
|
arguments_digest: digest(input),
|
|
46
46
|
governance_plan_hash: bound.plan.plan_hash, mechanism_stamp: bound.stamp, governance_activation_id: bound.activation_id, gate_id: gate.id, decision_id: gate.decision_id,
|
|
47
47
|
allowed, reason: result?.reason || (allowed ? 'Bound mechanism passed.' : 'Bound mechanism refused.') });
|
|
48
|
-
if (!allowed) throw Object.assign(new Error(`
|
|
48
|
+
if (!allowed) throw Object.assign(new Error(`Governance refused ${op.contract.name}: ${result?.reason || gate.id}`), { code: 'GOVERNANCE_REFUSED' });
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
async fact(context, event) { const row = { ...event, house: this.cwd, session_id: context.sessionId, actor: context.actor, surface: context.surface }; await this.record(row, context); return row; }
|
|
52
52
|
// Bind identities in host code, never in model-supplied operation arguments.
|
|
53
53
|
bind({ actor, sessionId, policy, surface = 'agent', onPermission = async () => {}, record, resources = {}, allowed, select = () => true, frontier, capture, unavailable }) {
|
|
54
|
-
if (!actor?.id || !['human', 'agent', 'subagent'].includes(actor.kind) || !policy?.authorize) throw new Error('
|
|
54
|
+
if (!actor?.id || !['human', 'agent', 'subagent'].includes(actor.kind) || !policy?.authorize) throw new Error('The house requires a host-bound actor and policy.');
|
|
55
55
|
const context = Object.freeze({ actor: Object.freeze(structuredClone(actor)), get sessionId() { return typeof sessionId === 'function' ? sessionId() : sessionId; }, policy, surface, onPermission, record, resources, frontier, unavailable });
|
|
56
56
|
const names = allowed ? new Set(allowed) : null;
|
|
57
57
|
const snapshot = () => new Map([...this.registry.snapshot(context)].filter(([name]) => (!names || names.has(name)) && select(name,context)));
|
|
@@ -78,10 +78,10 @@ export class House {
|
|
|
78
78
|
for (const { id, check } of this.#guards.values()) {
|
|
79
79
|
signal?.throwIfAborted(); let result;
|
|
80
80
|
try { result = await check(view); }
|
|
81
|
-
catch (error) { if (signal?.aborted) throw error; result = { allowed: false, reason: `
|
|
81
|
+
catch (error) { if (signal?.aborted) throw error; result = { allowed: false, reason: `Guard unavailable: ${error.message}` }; }
|
|
82
82
|
if (result === false || result?.allowed === false) {
|
|
83
|
-
await this.emit(context, { type: 'house_decision', invocation_id: this.#context.getStore()?.id, contract_digest: op.digest, operation: op.contract.name, arguments_digest: digest(args), allowed: false, reason: result?.reason || `
|
|
84
|
-
throw Object.assign(new Error(`
|
|
83
|
+
await this.emit(context, { type: 'house_decision', invocation_id: this.#context.getStore()?.id, contract_digest: op.digest, operation: op.contract.name, arguments_digest: digest(args), allowed: false, reason: result?.reason || `Additional veto from ${id}`, source: 'plugin_guard', guard: id, mode: context.policy.mode });
|
|
84
|
+
throw Object.assign(new Error(`Permission denied: ${result?.reason || `additional veto from ${id}`}`), { code: 'HOUSE_GUARD' });
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -94,14 +94,14 @@ export class House {
|
|
|
94
94
|
const result = await this.#decision(context, name, input, signal, decision);
|
|
95
95
|
if (before === this.authority(context)) { const active = this.#context.getStore(); if (active) active.authorityStamp = before; return result; }
|
|
96
96
|
}
|
|
97
|
-
throw Object.assign(new Error('
|
|
97
|
+
throw Object.assign(new Error('Permission denied: authority changed during judgment; propose again.'), { code: 'HOUSE_AUTHORITY_CHANGED' });
|
|
98
98
|
}
|
|
99
99
|
async #decision(context, name, input, signal, decision) {
|
|
100
100
|
await context.onPermission({ name, input, ...decision });
|
|
101
101
|
const active = this.#context.getStore();
|
|
102
102
|
await this.emit(context, { type: 'house_decision', invocation_id: active?.id ?? null, contract_digest: active?.contractDigest ?? null, operation: name, arguments_digest: digest(input), allowed: Boolean(decision.allowed), reason: decision.reason, mode: context.policy.mode });
|
|
103
103
|
if (active) active.decisions.push(Boolean(decision.allowed));
|
|
104
|
-
if (!decision.allowed) { const error = new Error(`
|
|
104
|
+
if (!decision.allowed) { const error = new Error(`Permission denied: ${name}: ${decision.reason}`); error.code = 'HOUSE_REFUSED'; throw error; }
|
|
105
105
|
signal?.throwIfAborted(); return decision;
|
|
106
106
|
}
|
|
107
107
|
async execute(context, catalog, name, input, { signal, callId, work, beforeEffect, modelProposed = false } = {}) {
|
|
@@ -122,43 +122,43 @@ export class House {
|
|
|
122
122
|
const recovery = context.unavailable?.(name,context);
|
|
123
123
|
if (recovery) throw Object.assign(new Error(recovery.message), {code:recovery.code});
|
|
124
124
|
}
|
|
125
|
-
if (!op || !op.visible(context)) throw Object.assign(new Error(`
|
|
126
|
-
if (this.registry.get(name) !== op) throw Object.assign(new Error('
|
|
125
|
+
if (!op || !op.visible(context)) throw Object.assign(new Error(`Permission denied: operation ${name} is unavailable to this actor/surface.${context.actor.kind === 'subagent' ? ' Recursive delegation and tools outside the scope are disabled.' : ''}`), { code: 'HOUSE_REFUSED' });
|
|
126
|
+
if (this.registry.get(name) !== op) throw Object.assign(new Error('The operation contract changed after this proposal; it was not executed.'), { code: 'HOUSE_STALE' });
|
|
127
127
|
op.validate(args); signal?.throwIfAborted();
|
|
128
|
-
if (modelProposed && name === 'Edit' && args.old_string === args.new_string) throw Object.assign(new Error('
|
|
129
|
-
if (modelProposed && ((name === 'Write' && args.content.length > 6000) || (name === 'Edit' && (args.old_string.length > 3000 || args.new_string.length > 6000)))) throw Object.assign(new Error('
|
|
128
|
+
if (modelProposed && name === 'Edit' && args.old_string === args.new_string) throw Object.assign(new Error('Edit has no changes: old_string and new_string are identical. It was not executed or sent to the classifier; skip this step or propose a real correction.'), { code: 'MODEL_NO_CHANGE' });
|
|
129
|
+
if (modelProposed && ((name === 'Write' && args.content.length > 6000) || (name === 'Edit' && (args.old_string.length > 3000 || args.new_string.length > 6000)))) throw Object.assign(new Error('File proposal is too large for one model step. Use a focused Edit (old_string <=3000, new_string <=6000), or WorkDraft append with a stable part_id followed by publish. The edit was not executed.'), {code:'MODEL_STEP_TOO_LARGE'});
|
|
130
130
|
return await this.#context.run(state, async () => {
|
|
131
|
-
if(op.contract.permission!==name && context.policy.denied?.has(name))await this.#decision(context,name,args,signal,{allowed:false,reason:'
|
|
132
|
-
if (op.contract.permission === null) await this.#decision(context, name, args, signal, { allowed: !context.policy.denied?.has(name), reason: '
|
|
131
|
+
if((op.contract.steps || op.contract.permission!==name) && context.policy.denied?.has(name))await this.#decision(context,name,args,signal,{allowed:false,reason:'Explicit operation denial; composition and aliases cannot widen authority.'});
|
|
132
|
+
if (op.contract.permission === null) await this.#decision(context, name, args, signal, { allowed: !context.policy.denied?.has(name), reason: 'Explicit user control; does not grant authority to the model.' });
|
|
133
133
|
else if (!op.contract.steps) await this.judge(context, op.contract.permission, args, { signal });
|
|
134
134
|
if (op.contract.permission !== null) await this.guardOperation(context, op, args, signal);
|
|
135
135
|
await this.govern(context, op, args, state);
|
|
136
136
|
if (beforeEffect) await beforeEffect();
|
|
137
137
|
// Permission callbacks may reload the catalogue. Revalidate at the effect frontier.
|
|
138
|
-
if (this.registry.get(name) !== op) throw Object.assign(new Error('
|
|
138
|
+
if (this.registry.get(name) !== op) throw Object.assign(new Error('The contract changed during judgment; it was not executed.'), { code: 'HOUSE_STALE' });
|
|
139
139
|
signal?.throwIfAborted(); await this.emit(context, { type: 'house_started', ...base });
|
|
140
140
|
this.#active.add(id);
|
|
141
141
|
signal?.throwIfAborted();
|
|
142
|
-
if (this.registry.get(name) !== op) throw Object.assign(new Error('
|
|
143
|
-
if (!op.contract.steps && op.contract.permission !== null && state.authorityStamp !== this.authority(context)) throw Object.assign(new Error('
|
|
142
|
+
if (this.registry.get(name) !== op) throw Object.assign(new Error('The contract changed before the effect; it was not executed.'), { code: 'HOUSE_STALE' });
|
|
143
|
+
if (!op.contract.steps && op.contract.permission !== null && state.authorityStamp !== this.authority(context)) throw Object.assign(new Error('Permission denied: authority changed before the effect.'), { code: 'HOUSE_AUTHORITY_CHANGED' });
|
|
144
144
|
if (beforeEffect) await beforeEffect(); signal?.throwIfAborted();
|
|
145
145
|
// A work-state check can await persistence. Contracts and authority must
|
|
146
146
|
// still match after that await, immediately before entering the handler.
|
|
147
|
-
if (this.registry.get(name) !== op) throw Object.assign(new Error('
|
|
148
|
-
if (!op.contract.steps && op.contract.permission !== null && state.authorityStamp !== this.authority(context)) throw Object.assign(new Error('
|
|
149
|
-
if (context.frontier && await context.frontier(name, context) === false) throw Object.assign(new Error('
|
|
147
|
+
if (this.registry.get(name) !== op) throw Object.assign(new Error('The contract changed before the effect; it was not executed.'), { code: 'HOUSE_STALE' });
|
|
148
|
+
if (!op.contract.steps && op.contract.permission !== null && state.authorityStamp !== this.authority(context)) throw Object.assign(new Error('Permission denied: authority changed before the effect.'), { code: 'HOUSE_AUTHORITY_CHANGED' });
|
|
149
|
+
if (context.frontier && await context.frontier(name, context) === false) throw Object.assign(new Error('Permission denied: the tool lease changed before the effect.'), { code: 'HOUSE_SURFACE_CHANGED' });
|
|
150
150
|
signal?.throwIfAborted();
|
|
151
|
-
if (this.registry.get(name) !== op) throw Object.assign(new Error('
|
|
152
|
-
if (!op.contract.steps && op.contract.permission !== null && state.authorityStamp !== this.authority(context)) throw Object.assign(new Error('
|
|
153
|
-
if (digest(governance) !== digest(this.governance(context.sessionId))) throw Object.assign(new Error('
|
|
151
|
+
if (this.registry.get(name) !== op) throw Object.assign(new Error('The contract changed before the effect; it was not executed.'), { code: 'HOUSE_STALE' });
|
|
152
|
+
if (!op.contract.steps && op.contract.permission !== null && state.authorityStamp !== this.authority(context)) throw Object.assign(new Error('Permission denied: authority changed before the effect.'), { code: 'HOUSE_AUTHORITY_CHANGED' });
|
|
153
|
+
if (digest(governance) !== digest(this.governance(context.sessionId))) throw Object.assign(new Error('Governance changed before the effect; propose again.'), { code: 'HOUSE_AUTHORITY_CHANGED' });
|
|
154
154
|
entered = true;
|
|
155
155
|
const output = await op.run(args, { ...context.resources, invocation_id:id, sessionId: context.sessionId, actor: context.actor, mode: context.policy.mode, catalogue: [...catalog.values()].map(item => ({ ...item.contract, contract_digest: item.digest })), signal,
|
|
156
156
|
recordWork: event => this.emit(context, { ...event, invocation_id: id }),
|
|
157
157
|
invoke: (operation, arguments_, options) => this.execute(context, catalog, operation, arguments_, options),
|
|
158
158
|
observe: fact => state.observations.push(structuredClone(fact)) });
|
|
159
|
-
if (state.observations.some(fact => fact.kind === 'file_write' && fact.matches_requested === false)) throw Object.assign(new Error('
|
|
159
|
+
if (state.observations.some(fact => fact.kind === 'file_write' && fact.matches_requested === false)) throw Object.assign(new Error('The written surface does not match the requested result; inspect before repeating.'), { code: 'HOUSE_POSTCONDITION' });
|
|
160
160
|
op.validateOutput(output);
|
|
161
|
-
const text = typeof output === 'string' ? output : JSON.stringify(output) ?? '(
|
|
161
|
+
const text = typeof output === 'string' ? output : JSON.stringify(output) ?? '(no output)';
|
|
162
162
|
await this.emit(context, { type: 'house_receipt', ...base, status: 'returned', disposition: state.decisions.includes(false) ? 'basket' : 'serve', judgment_scope: 'house_execution', authority: op.contract.permission === null ? 'human_control' : op.contract.steps ? (state.decisions.includes(false) ? 'step_refused' : state.decisions.length ? 'steps_judged' : 'no_permission_step') : 'allowed', permission_steps: { allowed: state.decisions.filter(Boolean).length, refused: state.decisions.filter(value => !value).length }, output_digest: digest(text), output: text, output_truncated: false, observations: state.observations, effects: op.contract.effects, asked: 'unjudged' });
|
|
163
163
|
return text;
|
|
164
164
|
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Original host messages for the optional Spanish interface. No user/model content.
|
|
2
|
+
export default Object.freeze({
|
|
3
|
+
"Session saved: {v0}\nResume: node bin/comal.mjs --resume {v1}\n": "Sesión guardada: {v0}\nReanudar: node bin/comal.mjs --resume {v1}\n",
|
|
4
|
+
"Comal: opens {v0}\nCheck the code {v1} and approve only if you initiated this connection.\n": "Comal: abre {v0}\nComprueba el código {v1} y aprueba sólo si tú iniciaste esta conexión.\n",
|
|
5
|
+
"Use comal cloud login|status|logout [--cloud-url ORIGIN].": "Usa comal cloud login|status|logout [--cloud-url ORIGEN].",
|
|
6
|
+
"Use comal artifacts publish DIRECTORY --manifest METADATA.json.": "Usa comal artifacts publish DIRECTORIO --manifest METADATA.json.",
|
|
7
|
+
"The manifest must be a JSON object.": "El manifiesto debe ser un objeto JSON.",
|
|
8
|
+
"Field not allowed in the manifest: ": "Campo no admitido en el manifiesto: ",
|
|
9
|
+
"Use comal artifacts list|inspect ID|verify ID|publish DIR --manifest JSON|release ID VERSION JOB.": "Usa comal artifacts list|inspect ID|verify ID|publish DIR --manifest JSON|release ID VERSION JOB.",
|
|
10
|
+
"Login {v0}: {v1} · code {v2}": "Login {v0}: {v1} · código {v2}",
|
|
11
|
+
"Use comal login PROVIDER, comal logout PROVIDER, or comal auth status [PROVIDER].": "Usa comal login PROVEEDOR, comal logout PROVEEDOR o comal auth status [PROVEEDOR].",
|
|
12
|
+
"Use comal message DESTINATION TEXT.": "Usa comal message DESTINO TEXTO.",
|
|
13
|
+
"Use audit --resume UUID or audit --continue.": "Usa audit --resume UUID o audit --continue.",
|
|
14
|
+
"--work-max-calls must be between 0 and 10000.": "--work-max-calls debe estar entre 0 y 10000.",
|
|
15
|
+
"--max-turns must be between 1 and 100.": "--max-turns debe estar entre 1 y 100.",
|
|
16
|
+
"--output-format invalid.": "--output-format inválido.",
|
|
17
|
+
"--permission-mode invalid.": "--permission-mode inválido.",
|
|
18
|
+
"Use --goal or a prompt, not both.": "Usa --goal o un prompt, no ambos.",
|
|
19
|
+
"No sessions to continue.": "No hay sesiones para continuar.",
|
|
20
|
+
"This session belongs to a subagent; open the parent session and use /agents to review it.": "Esta sesión es de un subagente; abre la sesión padre y usa /agents para revisarla.",
|
|
21
|
+
"Altered book: use comal audit --resume UUID to inspect it without a model or MCP connections.": "Libro alterado: usa comal audit --resume UUID para inspeccionarlo sin modelo ni conexiones MCP.",
|
|
22
|
+
"Missing a valid reference to the skill's original provider; restoration was not guessed.": "Falta una referencia válida al proveedor original de la skill; no se adivinó restauración.",
|
|
23
|
+
"The saved assignment is immutable; use a new session.": "El encargo guardado es inmutable; usa una sesión nueva.",
|
|
24
|
+
"--auth-provider {v0} selects its connection; profile {v1} does not apply. The session name is preserved.": "--auth-provider {v0} selecciona su conexión; el perfil {v1} no se aplica. El nombre de sesión se conserva.",
|
|
25
|
+
"Subscription login is only sent to the provider's origin. Use another profile for custom endpoints.": "El login de suscripción sólo se envía al origen del proveedor. Usa otro perfil para endpoints personalizados.",
|
|
26
|
+
"Credential destination outside the provider.": "Destino de credenciales fuera del proveedor.",
|
|
27
|
+
"--name must be 1 to 120 characters.": "--name debe tener de 1 a 120 caracteres.",
|
|
28
|
+
"--inbound must be accept, hold, or refuse.": "--inbound debe ser accept, hold o refuse.",
|
|
29
|
+
"◇ Active model · {v0} s · reasoning {v1} chars · arguments {v2} chars\n": "◇ Modelo activo · {v0} s · razonamiento {v1} chars · argumentos {v2} chars\n",
|
|
30
|
+
"\n◇ Deriving book state…\n": "\n◇ Derivando estado del libro…\n",
|
|
31
|
+
"✓ Derived state: ~{v0} tokens; 0 model summary calls.\n": "✓ Estado derivado: ~{v0} tokens; 0 llamadas de resumen al modelo.\n",
|
|
32
|
+
"◉ Vision: {v0} images in the request · reserves ~{v1} tokens · {v2}.\n": "◉ Visión: {v0} imágenes en la petición · reserva ~{v1} tokens · {v2}.\n",
|
|
33
|
+
"does not fit in the context": "no cabe en el contexto",
|
|
34
|
+
"provider without announced vision": "proveedor sin visión anunciada",
|
|
35
|
+
"◇ Continues {v0} · {v1}/{v2} calls · checkpoint preserved.\n": "◇ Continúa {v0} · {v1}/{v2} llamadas · checkpoint conservado.\n",
|
|
36
|
+
"◇ Open work: {v0} · /work resume preserves progress.\n": "◇ Trabajo abierto: {v0} · /work resume conserva el avance.\n",
|
|
37
|
+
"◇ Response discarded ({v0}); bounded retry with {v1} output tokens.\n": "◇ Respuesta descartada ({v0}); reintento acotado con {v1} tokens de salida.\n",
|
|
38
|
+
"session": "sesión",
|
|
39
|
+
"◇ Report to mailbox {v0} · {v1}{v2}\n": "◇ Informe al buzón {v0} · {v1}{v2}\n",
|
|
40
|
+
"Session {v0} · {v1} calls · {v2} tokens": "Sesión {v0} · {v1} llamadas · {v2} tokens",
|
|
41
|
+
"Duplicate tool: {v0}": "Herramienta duplicada: {v0}",
|
|
42
|
+
"The delegated budget belongs to another session.": "El presupuesto delegado pertenece a otra sesión.",
|
|
43
|
+
"The child model is not announced by this provider.": "El modelo hijo no está anunciado por este proveedor.",
|
|
44
|
+
"Connecting MCP {v0} · {v1}": "Conectando MCP {v0} · {v1}",
|
|
45
|
+
"Plugin operation not published.": "Operación de plugin no publicada.",
|
|
46
|
+
"(catalog synchronized)": "(catálogo sincronizado)",
|
|
47
|
+
"No goal. Use /goal <condition>.": "No hay objetivo. Usa /goal <condición>.",
|
|
48
|
+
"No goal. Use /goal <condition>.\n": "No hay objetivo. Usa /goal <condición>.\n",
|
|
49
|
+
"No goal.": "No hay objetivo.",
|
|
50
|
+
"Paused by the user.": "Pausado por el usuario.",
|
|
51
|
+
"Goal canceled by the user.": "Objetivo cancelado por el usuario.",
|
|
52
|
+
"Goal resumed with its remaining budget.": "Objetivo reanudado con su presupuesto restante.",
|
|
53
|
+
"No pending work to continue.": "No hay trabajo pendiente para continuar.",
|
|
54
|
+
"No open work to continue.": "No hay trabajo abierto para continuar.",
|
|
55
|
+
"Use /send ID_OR_NAME TEXT.": "Usa /send ID_O_NOMBRE TEXTO.",
|
|
56
|
+
"Use /compact without arguments.": "Usa /compact sin argumentos.",
|
|
57
|
+
"Context prepared: ~{v0}/{v1} tokens; {v2} reserved for output. State derived from the ledger without LLM; original history preserved.": "Contexto preparado: ~{v0}/{v1} tokens; {v2} reservados para salida. Estado derivado del libro sin LLM; historial original conservado.",
|
|
58
|
+
"Use /search QUERY.": "Usa /search CONSULTA.",
|
|
59
|
+
"Use /agent [explore|general] TASK.": "Usa /agent [explore|general] TAREA.",
|
|
60
|
+
"Unknown command: /{v0}. Use /help.": "Comando desconocido: /{v0}. Usa /help.",
|
|
61
|
+
"Follow the skill.": "Sigue la skill.",
|
|
62
|
+
"The skill requests a model not advertised by this provider.": "La skill solicita un modelo no anunciado por este proveedor.",
|
|
63
|
+
"Pending message: {v0}\n": "Mensaje pendiente: {v0}\n",
|
|
64
|
+
"Subagents require a real model; --demo is a script.": "Los subagentes requieren un modelo real; --demo es un guion.",
|
|
65
|
+
"### Search: {v0}\n\nProvider: {v1}\n\n": "### Búsqueda: {v0}\n\nProveedor: {v1}\n\n",
|
|
66
|
+
"No matches.": "Sin coincidencias.",
|
|
67
|
+
"Permissions: {v0}": "Permisos: {v0}",
|
|
68
|
+
"Use /model ID, without spaces.": "Usa /model ID, sin espacios.",
|
|
69
|
+
"Model: {v0} · effort {v1} · same session": "Modelo: {v0} · effort {v1} · misma sesión",
|
|
70
|
+
"Use /rename NAME.": "Usa /rename NOMBRE.",
|
|
71
|
+
"Session: {v0}": "Sesión: {v0}",
|
|
72
|
+
"The demo does not allow changing provider.": "La demo no permite cambiar proveedor.",
|
|
73
|
+
"Provider: {v0} · {v1} · {v2}": "Proveedor: {v0} · {v1} · {v2}",
|
|
74
|
+
"Use /inbox [accept|hold|refuse|release ID|all].": "Usa /inbox [accept|hold|refuse|release ID|all].",
|
|
75
|
+
"Use a .csv, .tsv, .json, or .md file.": "Usa un archivo .csv, .tsv, .json o .md.",
|
|
76
|
+
"Change model/effort in a session with a real provider.": "Cambia modelo/esfuerzo en una sesión con proveedor real.",
|
|
77
|
+
"Use /login PROVIDER [--api-key-env VARIABLE].": "Usa /login PROVEEDOR [--api-key-env VARIABLE].",
|
|
78
|
+
"Conversation cleared by the user.": "Conversación limpiada por el usuario.",
|
|
79
|
+
"This session belongs to a subagent; use /agents in its parent session.": "Esta sesión es de un subagente; usa /agents en su sesión padre.",
|
|
80
|
+
"Use /logout PROVIDER.": "Usa /logout PROVEEDOR.",
|
|
81
|
+
"Use /login PROVIDER [--api-key-env VARIABLE]. Never paste keys into the composer.": "Usa /login PROVEEDOR [--api-key-env VARIABLE]. Nunca pegues claves en el composer.",
|
|
82
|
+
"Use /capabilities [refresh].": "Usa /capabilities [refresh].",
|
|
83
|
+
"/work recover requires an accepted Chrome task.": "/work recover requiere una tarea Chrome aceptada.",
|
|
84
|
+
"Estimated context: ~{v0}/{v1} tokens\nAdvertised window: {v2} · explicit limit: {v3} · effective source: {v4}\nOutput reserve: {v5} · margin: {v6} · total: {v7}%\nActive messages: {v8} · original: {v9}\nProjection: {v10}\nUse /state, /audit, or /compact to derive the state of the ledger.": "Contexto estimado: ~{v0}/{v1} tokens\nVentana anunciada: {v2} · límite explícito: {v3} · fuente efectiva: {v4}\nReserva de salida: {v5} · margen: {v6} · total: {v7}%\nMensajes activos: {v8} · originales: {v9}\nProyección: {v10}\nUsa /state, /audit o /compact para derivar el estado del libro.",
|
|
85
|
+
"(not yet derived)": "(aún no derivada)",
|
|
86
|
+
"Use /agents ": "Usa /agents ",
|
|
87
|
+
"Subagent not found in this session.": "Subagente no encontrado en esta sesión.",
|
|
88
|
+
"There are no subagents in this session. Use /agent TASK or ask the model to delegate.": "No hay subagentes en esta sesión. Usa /agent TAREA o pide al modelo que delegue.",
|
|
89
|
+
"Workspace: {v0}\nProvider: {v1}\nEndpoint: {v2}\nModel: {v3}\nPermissions: {v4}\nSession: {v5}\nMessages: {v6} active / {v7} original\nContext: ~{v8}/{v9} · output {v10}\nModel timeout: {v11}\nGoal: {v12}": "Workspace: {v0}\nProveedor: {v1}\nEndpoint: {v2}\nModelo: {v3}\nPermisos: {v4}\nSesión: {v5}\nMensajes: {v6} activos / {v7} originales\nContexto: ~{v8}/{v9} · salida {v10}\nTimeout modelo: {v11}\nGoal: {v12}",
|
|
90
|
+
"(local without API)": "(local sin API)",
|
|
91
|
+
"(demo without API)": "(demo sin API)",
|
|
92
|
+
" / {v0} ms without progress": " / {v0} ms sin progreso",
|
|
93
|
+
"Use /permissions default|plan|acceptEdits|auto.": "Usa /permissions default|plan|acceptEdits|auto.",
|
|
94
|
+
"Model: {v0} · effort {v1}\nUse /models, /model ID, or /effort LEVEL.": "Modelo: {v0} · effort {v1}\nUsa /models, /model ID o /effort NIVEL.",
|
|
95
|
+
"There are no saved sessions.\n": "No hay sesiones guardadas.\n",
|
|
96
|
+
"Comal · standalone CLI programming assistant\n\nUsage: node bin/comal.mjs [options] [prompt]\n node bin/comal.mjs sessions | evidence | audit --resume UUID\n\n -p, --print Runs a turn and exits (also accepts stdin)\n --demo Deterministic demo without credentials; allows its edits\n --cwd <path> Workspace (default: current directory)\n --provider <protocol> openai (default) | anthropic | gemini\n --profile <name> Profile from .comal/providers.json (endpoint/model/capabilities)\n --providers-config <file> Profiles file; keys only via apiKeyEnv\n --name <name> Name visible to other sessions\n --listen Keeps the CLI listening for messages and responds when idle\n --inbound <mode> accept (default) | hold | refuse\n --auth-provider <id> Comal login: codex | kimi | minimax | grok\n --effort <level> auto | off | minimal | low | medium | high | xhigh | max (if the model supports it)\n --model <id> Model; by default discovers the local one at /v1/models\n --base-url <url> Endpoint; default http://llama.local:11438/\n --permission-mode <mode> default | acceptEdits | plan | auto\n --allowed-tools <names> Authorizes comma-separated names\n --disallowed-tools <names> Denies names; overrides authorization\n --resume <uuid> Resumes session; with -p and no text continues pending work\n -c, --continue Resumes the most recent session\n --output-format <format> text | json | stream-json (with --print)\n --max-turns <n> Calls before extending work with progress (12)\n --work-max-calls <n> Total limit with task continuation (80; 0 disables)\n --context-window <n> Explicit limit; automatic based on model, fallback 45000\n --max-tokens <n> Initial maximum output tokens (8192)\n --thinking <mode> auto | on | off | low | medium | high (llama.cpp)\n --vision <mode> auto | on | off (auto queries /props when receiving images)\n --image-tokens <n> Estimated reservation per image (4096, maximum 2 images)\n --model-timeout-ms <n> Total time for each OpenAI response (600000)\n --model-idle-timeout-ms <n> Time without OpenAI token progress (180000)\n --system-prompt <text> Additional instructions\n --state-dir <path> State location; default .comal/ (reuses existing .harness/)\n --mcp-config <file> Connects MCP stdio, Streamable HTTP, or SSE from a JSON\n --trust-mcp Authorizes initial connections; changes are reviewed again\n --plugin-config <file> Local Node plugins (JSON); default .comal/plugins.json\n --lang <en|es|auto> UI language (default: en; auto opts into locale detection)\n --codemode Read-only JavaScript tool composition (default: on)\n --no-codemode Disable CodeRun for this process\n --trust-plugins Authorizes loading local code from the initial config\n --chrome-task <file.json> Explicit Chrome task; the house coordinates publication, checks, and closure\n --goal <condition> Runs a goal until verification or limit\n --goal-max-turns <n> Maximum goal turns (20)\n --goal-max-calls <n> Total calls: agent + verifiers (80)\n --goal-max-tokens <n> Goal token budget (100000)\n -h, --help Shows this help\n -v, --version Shows version\n\nVariables (HARNESS_* remains as alias): COMAL_PROVIDER, COMAL_BASE_URL, COMAL_MODEL,\n COMAL_CONTEXT_WINDOW, COMAL_MAX_TOKENS, COMAL_WORK_MAX_CALLS,\n COMAL_MODEL_TIMEOUT_MS, COMAL_MODEL_IDLE_TIMEOUT_MS,\n OPENAI_BASE_URL, OPENAI_MODEL, OPENAI_API_KEY (optional for local),\n ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL (with --provider anthropic).\nRead authorized by default; write and Bash require permission.\nIn --print without a terminal, unauthorized tools are denied.\nShift+Tab: default → acceptEdits → plan → auto → default.\nInteractive commands: /help /status /tools /house /operations /sessions /resume UUID /clear\n /compact /context /state /audit /board /work resume|recover\n /model ID /permissions MODE /goal [condition] /exit\n /queue [pause|resume|clear|edit N] /rename NAME /table FILE\nTUI: F1 help · F2 sessions · F4 queue · F5 or /select selection · F6 discover commands/tools.\n / and Tab complete; ↑/↓ chooses; Enter on suggestions only completes. Alt+C or /copy copies.\n Ctrl+G editor · Alt+Enter multiline.\nMCP: /mcp · /mcp reload [file] · /mcp add NAME {JSON} · /mcp remove NAME\n /mcp reconnect NAME. Default file: .mcp.json in the workspace.\nWeb: /search QUERY · WebSearch/WebFetch · COMAL_SEARCH_PROVIDER, SEARXNG_URL, BRAVE_SEARCH_API_KEY.\nSubagents: /agent [explore|general] TASK · /agents [UUID] · Agent/AgentBatch.\nPlugins: /plugins [list|enable ID|disable ID|reload [ID]|run ID OPERATION {JSON}].\nLive sessions: /list-agents · /send ID_OR_NAME TEXT · /inbox [accept|hold|refuse|release ID|all]\nComal account: comal cloud login|status|logout [--cloud-url ORIGIN] (separate from the model provider)\nArtifacts: comal artifacts list|inspect ID|verify ID|publish DIR --manifest JSON|release ID VERSION JOB\nLogin: comal login PROVIDER [--api-key-env VARIABLE] · comal auth status [PROVIDER] · comal logout PROVIDER\nModels: /model [ID] · /models · /effort [LEVEL] · /capabilities [refresh] · /login PROVIDER · /logout PROVIDER\nProviders: /providers · /provider PROFILE · ProviderSearch/AnalyzeMedia according to capabilities.\nCLI: comal peers | providers | message DESTINATION TEXT (use --listen to receive in CLI).\n/goal status|pause|resume|clear controls the goal; Ctrl+C pauses it.\nLocal skills: .comal/skills/<name>/SKILL.md → /<name> [task]\n\nRemote Control: /rc [start|status|stop] · TUI account: /cloud [login|status|logout] · CLI: comal cloud login\n": "Comal · asistente de programación CLI independiente\n\nUso: node bin/comal.mjs [opciones] [prompt]\n node bin/comal.mjs sessions | evidence | audit --resume UUID\n\n -p, --print Ejecuta un turno y termina (también acepta stdin)\n --demo Demo determinista sin credenciales; permite sus ediciones\n --cwd <ruta> Workspace (por defecto: directorio actual)\n --provider <protocolo> openai (predeterminado) | anthropic | gemini\n --profile <nombre> Perfil de .comal/providers.json (endpoint/modelo/capacidades)\n --providers-config <archivo> Archivo de perfiles; claves sólo vía apiKeyEnv\n --name <nombre> Nombre visible a otras sesiones\n --listen Mantiene el CLI escuchando mensajes y responde al quedar idle\n --inbound <modo> accept (predeterminado) | hold | refuse\n --auth-provider <id> Login de Comal: codex | kimi | minimax | grok\n --effort <nivel> auto | off | minimal | low | medium | high | xhigh | max (si el modelo lo admite)\n --model <id> Modelo; por defecto descubre el local en /v1/models\n --base-url <url> Endpoint; por defecto http://llama.local:11438/\n --permission-mode <modo> default | acceptEdits | plan | auto\n --allowed-tools <nombres> Autoriza nombres separados por comas\n --disallowed-tools <nombres> Deniega nombres; prevalece sobre autorización\n --resume <uuid> Reanuda sesión; con -p sin texto continúa trabajo pendiente\n -c, --continue Reanuda la sesión más reciente\n --output-format <formato> text | json | stream-json (con --print)\n --max-turns <n> Llamadas antes de extender trabajo con progreso (12)\n --work-max-calls <n> Límite total con continuación de tareas (80; 0 desactiva)\n --context-window <n> Límite explícito; automático según modelo, fallback 45000\n --max-tokens <n> Máximo inicial de tokens de salida (8192)\n --thinking <modo> auto | on | off | low | medium | high (llama.cpp)\n --vision <modo> auto | on | off (auto consulta /props al recibir imágenes)\n --image-tokens <n> Reserva estimada por imagen (4096, máximo 2 imágenes)\n --model-timeout-ms <n> Tiempo total de cada respuesta OpenAI (600000)\n --model-idle-timeout-ms <n> Tiempo sin progreso de tokens OpenAI (180000)\n --system-prompt <texto> Instrucciones adicionales\n --state-dir <ruta> Ubicación del estado; por defecto .comal/ (reutiliza .harness/ existente)\n --mcp-config <archivo> Conecta MCP stdio, Streamable HTTP o SSE de un JSON\n --trust-mcp Autoriza conexiones iniciales; cambios se revisan de nuevo\n --plugin-config <archivo> Plugins Node locales (JSON); por defecto .comal/plugins.json\n --trust-plugins Autoriza cargar código local de la config inicial\n --codemode Experimental read-only JavaScript tool composition\n --chrome-task <archivo.json> Tarea Chrome explícita; la casa coordina publicación, checks y cierre\n --goal <condición> Ejecuta un objetivo hasta verificación o límite\n --goal-max-turns <n> Máximo de turnos del objetivo (20)\n --goal-max-calls <n> Llamadas totales: agente + verificadores (80)\n --goal-max-tokens <n> Presupuesto de tokens del objetivo (100000)\n -h, --help Muestra esta ayuda\n -v, --version Muestra versión\n\nVariables (HARNESS_* sigue como alias): COMAL_PROVIDER, COMAL_BASE_URL, COMAL_MODEL,\n COMAL_CONTEXT_WINDOW, COMAL_MAX_TOKENS, COMAL_WORK_MAX_CALLS,\n COMAL_MODEL_TIMEOUT_MS, COMAL_MODEL_IDLE_TIMEOUT_MS,\n OPENAI_BASE_URL, OPENAI_MODEL, OPENAI_API_KEY (opcional para local),\n ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL (con --provider anthropic).\nLectura autorizada por defecto; escritura y Bash requieren permiso.\nEn --print sin terminal, las herramientas no autorizadas se deniegan.\nShift+Tab: default → acceptEdits → plan → auto → default.\nRemote Control: /rc [start|status|stop] · TUI account: /cloud [login|status|logout] · CLI: comal cloud login\nComandos interactivos: /help /status /tools /house /operations /sessions /resume UUID /clear\n /compact /context /state /audit /board /work resume|recover\n /model ID /permissions MODO /goal [condición] /exit\n /queue [pause|resume|clear|edit N] /rename NOMBRE /table ARCHIVO\nTUI: F1 ayuda · F2 sesiones · F4 cola · F5 o /select selección · F6 descubrir comandos/herramientas.\n / y Tab completan; ↑/↓ elige; Enter en sugerencias sólo completa. Alt+C o /copy copiar.\n Ctrl+G editor · Alt+Enter multilínea.\nMCP: /mcp · /mcp reload [archivo] · /mcp add NOMBRE {JSON} · /mcp remove NOMBRE\n /mcp reconnect NOMBRE. Archivo predeterminado: .mcp.json del workspace.\nWeb: /search CONSULTA · WebSearch/WebFetch · COMAL_SEARCH_PROVIDER, SEARXNG_URL, BRAVE_SEARCH_API_KEY.\nSubagentes: /agent [explore|general] TAREA · /agents [UUID] · Agent/AgentBatch.\nPlugins: /plugins [list|enable ID|disable ID|reload [ID]|run ID OPERACIÓN {JSON}].\nSesiones vivas: /list-agents · /send ID_O_NOMBRE TEXTO · /inbox [accept|hold|refuse|release ID|all]\nCuenta Comal: comal cloud login|status|logout [--cloud-url ORIGEN] (separada del proveedor de modelos)\nArtifacts: comal artifacts list|inspect ID|verify ID|publish DIR --manifest JSON|release ID VERSION JOB\nLogin: comal login PROVEEDOR [--api-key-env VARIABLE] · comal auth status [PROVEEDOR] · comal logout PROVEEDOR\nModelos: /model [ID] · /models · /effort [NIVEL] · /capabilities [refresh] · /login PROVEEDOR · /logout PROVEEDOR\nProveedores: /providers · /provider PERFIL · ProviderSearch/AnalyzeMedia según capacidades.\nCLI: comal peers | providers | message DESTINO TEXTO (usa --listen para recibir en CLI).\n/goal status|pause|resume|clear controla el objetivo; Ctrl+C lo pausa.\nSkills locales: .comal/skills/<nombre>/SKILL.md → /<nombre> [tarea]\n\n --lang <en|es|auto> Idioma de interfaz (predeterminado: en)\n\n --no-codemode Desactiva CodeRun para este proceso\n"
|
|
97
|
+
});
|
package/src/i18n/es.mjs
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// Host-authored interface text only. User/model/evidence content is never translated.
|
|
2
|
+
export default Object.freeze({
|
|
3
|
+
"Comal {v0} · Your first turn\n\nChoose how to start from your project:\n\n Try without account or model:\n {v1} --demo\n\n Connect Codex:\n {v2} login codex\n {v3} --auth-provider codex\n\n Use your OpenAI-compatible server:\n {v4} --base-url http://127.0.0.1:8080/v1 --model YOUR_MODEL\n\n Connect your Artifacts account (optional):\n {v5} cloud login\n\nGuide: https://comal.milpahq.com/academy/\n": "Comal {v0} · Tu primera vuelta\n\nElige cómo empezar desde tu proyecto:\n\n Probar sin cuenta ni modelo:\n {v1} --demo\n\n Conectar Codex:\n {v2} login codex\n {v3} --auth-provider codex\n\n Usar tu servidor compatible con OpenAI:\n {v4} --base-url http://127.0.0.1:8080/v1 --model TU_MODELO\n\n Conectar tu cuenta de Artifacts (opcional):\n {v5} cloud login\n\nGuía: https://comal.milpahq.com/academy/\n",
|
|
4
|
+
"Prompt exceeds 1 MiB.": "Prompt excede 1 MiB.",
|
|
5
|
+
"Every turn leaves a receipt.": "Cada vuelta deja un recibo.",
|
|
6
|
+
"Operations with intent.": "Operaciones con intención.",
|
|
7
|
+
"Could not write to the clipboard. Use F5, select with the mouse and copy from your terminal. On Linux you can install wl-clipboard (Wayland) or xclip (X11).": "No se pudo escribir al portapapeles. Usa F5, selecciona con el mouse y copia desde tu terminal. En Linux puedes instalar wl-clipboard (Wayland) o xclip (X11).",
|
|
8
|
+
"Code {v0}": "Código {v0}",
|
|
9
|
+
"Timed out.": "Tiempo de espera agotado.",
|
|
10
|
+
"No response text to copy.": "No hay texto de respuesta para copiar.",
|
|
11
|
+
"The composer supports up to 1 MiB.": "El composer admite hasta 1 MiB.",
|
|
12
|
+
"The editor produced more than 1 MiB; the previous draft is kept.": "El editor produjo más de 1 MiB; el borrador anterior se conserva.",
|
|
13
|
+
"The editor exited with code {v0}; the previous draft is kept.": "El editor terminó con código {v0}; el borrador anterior se conserva.",
|
|
14
|
+
"$VISUAL/$EDITOR must be an executable with arguments, without shell expansions.": "$VISUAL/$EDITOR debe ser un ejecutable con argumentos, sin expansiones de shell.",
|
|
15
|
+
"That message is already running or has left the queue.": "Ese mensaje ya se está ejecutando o salió de la cola.",
|
|
16
|
+
"Use /table file.csv|tsv|json|md.": "Usa /table archivo.csv|tsv|json|md.",
|
|
17
|
+
"Use /queue, /queue pause|resume|clear or /queue edit N.": "Usa /queue, /queue pause|resume|clear o /queue edit N.",
|
|
18
|
+
"Invalid message number.": "Número de mensaje inválido.",
|
|
19
|
+
"Use /{v0} without arguments.": "Usa /{v0} sin argumentos.",
|
|
20
|
+
"Cancel the turn with Esc or Ctrl+C before switching session or model.": "Cancela el turno con Esc o Ctrl+C antes de cambiar sesión o modelo.",
|
|
21
|
+
"The session is changing; keep the message and resend it.": "La sesión está cambiando; conserva el mensaje y vuelve a enviarlo.",
|
|
22
|
+
"The recovered queue is paused. Check F4 before resuming; interrupted messages were not resent.": "La cola recuperada está pausada. Revisa F4 antes de reanudar; los mensajes interrumpidos no se reenviaron.",
|
|
23
|
+
"F6 discovers commands and tools · / autocompletes · Ctrl+G opens your editor.": "F6 descubre comandos y herramientas · / autocompleta · Ctrl+G abre tu editor.",
|
|
24
|
+
"Plan mode: inspection without changes · Shift+Tab switches mode.": "Modo plan: inspección sin cambios · Shift+Tab cambia de modo.",
|
|
25
|
+
"Multiline message · Enter sends · Ctrl+G opens your editor.": "Mensaje multilínea · Enter envía · Ctrl+G abre tu editor.",
|
|
26
|
+
"You can keep typing · Enter queues · Esc interrupts the turn.": "Puedes seguir escribiendo · Enter encola · Esc interrumpe el turno.",
|
|
27
|
+
"Interrupted messages · F4 lets you review them before retrying.": "Hay mensajes interrumpidos · F4 permite revisarlos antes de reintentar.",
|
|
28
|
+
"Queue paused · F4 to review and resume it.": "Cola pausada · F4 para revisarla y reanudarla.",
|
|
29
|
+
"Editing a queued message · Enter saves · Esc keeps the original.": "Editando un mensaje en cola · Enter guarda · Esc conserva el original.",
|
|
30
|
+
"Select and copy with your terminal · F5 or Esc returns.": "Selecciona y copia con tu terminal · F5 o Esc vuelve.",
|
|
31
|
+
"The suggestion changed; select it again.": "La sugerencia cambió; vuelve a seleccionarla.",
|
|
32
|
+
"F6 opens available commands and tools.": "F6 abre comandos y herramientas disponibles.",
|
|
33
|
+
"Local skill: write the task after the name.": "Skill local: escribe la tarea después del nombre.",
|
|
34
|
+
"Never paste a key here; use --api-key-env VARIABLE.": "Nunca pegues una clave aquí; usa --api-key-env VARIABLE.",
|
|
35
|
+
"/plugins run ID OPERATION {JSON} · mutations wait their turn.": "/plugins run ID OPERACIÓN {JSON} · las mutaciones esperan su turno.",
|
|
36
|
+
"Only auto is confirmed. /capabilities shows the available levels.": "Sólo auto está confirmado. /capabilities muestra los niveles disponibles.",
|
|
37
|
+
"Use /models to check the catalog; no requests are made when typing.": "Usa /models para consultar el catálogo; no se hacen peticiones al escribir.",
|
|
38
|
+
"Operation registered by this plugin": "Operación registrada por este plugin",
|
|
39
|
+
"Environment variable name, never the key": "Nombre de variable de entorno, nunca la clave",
|
|
40
|
+
"Edit this pending message": "Editar este mensaje pendiente",
|
|
41
|
+
"Release held messages; does not change hold mode": "Liberar mensajes retenidos; no cambia el modo hold",
|
|
42
|
+
"Check login status": "Consultar el estado del login",
|
|
43
|
+
"Mailbox control between sessions": "Control del buzón entre sesiones",
|
|
44
|
+
"Subagent type; complete the task afterwards": "Tipo de subagente; completa después la tarea",
|
|
45
|
+
"Action on plugins": "Acción sobre plugins",
|
|
46
|
+
"Action on MCP servers": "Acción sobre servidores MCP",
|
|
47
|
+
"Action on the queue": "Acción sobre la cola",
|
|
48
|
+
"Target control; you can also write a condition": "Control del objetivo; también puedes escribir una condición",
|
|
49
|
+
"Continue work or reconcile a Chrome publication": "Continuar trabajo o reconciliar una publicación Chrome",
|
|
50
|
+
"Known live session; sending does not equal executing": "Sesión viva conocida; enviar no equivale a ejecutar",
|
|
51
|
+
"Child run controls": "Control de ejecuciones hijas",
|
|
52
|
+
"Level announced by the current model": "Nivel anunciado por el modelo actual",
|
|
53
|
+
"Permissions mode; does not change the model": "Modo de permisos; no cambia el modelo",
|
|
54
|
+
"Save and exit": "Guardar y salir",
|
|
55
|
+
"PROVIDER": "PROVEEDOR",
|
|
56
|
+
"Authenticate a provider without pasting keys": "Autenticar un proveedor sin pegar claves",
|
|
57
|
+
"Switch to the configured profile": "Cambiar al perfil configurado",
|
|
58
|
+
"PROFILE": "PERFIL",
|
|
59
|
+
"View profiles and login providers": "Ver perfiles y proveedores de login",
|
|
60
|
+
"Control the mailbox between sessions": "Controlar el buzón entre sesiones",
|
|
61
|
+
"[action]": "[acción]",
|
|
62
|
+
"Send a message to another session": "Enviar un mensaje a otra sesión",
|
|
63
|
+
"ID_OR_NAME TEXT": "ID_O_NOMBRE TEXTO",
|
|
64
|
+
"Discover other live sessions": "Descubrir otras sesiones vivas",
|
|
65
|
+
"Search the web": "Buscar en la web",
|
|
66
|
+
"QUERY": "CONSULTA",
|
|
67
|
+
"Delegate a task to a subagent": "Delegar una tarea a un subagente",
|
|
68
|
+
"[explore|general|background] TASK": "[explore|general|background] TAREA",
|
|
69
|
+
"Display a CSV, TSV, JSON, or Markdown table": "Mostrar una tabla CSV, TSV, JSON o Markdown",
|
|
70
|
+
"FILE": "ARCHIVO",
|
|
71
|
+
"Change the visible name of this session": "Cambiar el nombre visible de esta sesión",
|
|
72
|
+
"NAME": "NOMBRE",
|
|
73
|
+
"Manage queued messages": "Gestionar los mensajes en cola",
|
|
74
|
+
"Define or control a persistent goal": "Definir o controlar un objetivo persistente",
|
|
75
|
+
"[condition|status|pause|resume|clear]": "[condición|status|pause|resume|clear]",
|
|
76
|
+
"Change the permissions mode": "Cambiar el modo de permisos",
|
|
77
|
+
"MODE": "MODO",
|
|
78
|
+
"Support and provenance of capabilities": "Soporte y procedencia de las capacidades",
|
|
79
|
+
"Check or change the effort announced by the model": "Consultar o cambiar el esfuerzo anunciado por el modelo",
|
|
80
|
+
"LEVEL": "NIVEL",
|
|
81
|
+
"Check provider models; can make a request": "Consultar modelos del proveedor; puede hacer una petición",
|
|
82
|
+
"Check or change the model": "Consultar o cambiar el modelo",
|
|
83
|
+
"Open the task board": "Abrir el tablero de tareas",
|
|
84
|
+
"Request and current job status": "Petición y estado actual del trabajo",
|
|
85
|
+
"Check the context budget": "Consultar el presupuesto de contexto",
|
|
86
|
+
"Select text with the terminal": "Seleccionar texto con el terminal",
|
|
87
|
+
"Copy the last complete response": "Copiar la última respuesta completa",
|
|
88
|
+
"Create a new conversation": "Crear una conversación nueva",
|
|
89
|
+
"Resume a saved session": "Reanudar una sesión guardada",
|
|
90
|
+
"Open the session selector": "Abrir el selector de sesiones",
|
|
91
|
+
"Contracts and effects of operations": "Contratos y efectos de las operaciones",
|
|
92
|
+
"Receipts and interrupted operations": "Recibos y operaciones interrumpidas",
|
|
93
|
+
"Tools available at this time": "Herramientas disponibles en este momento",
|
|
94
|
+
"Session, provider and budget": "Sesión, proveedor y presupuesto",
|
|
95
|
+
"Help and keyboard": "Ayuda y teclado",
|
|
96
|
+
"CSV/TSV has an incomplete quoted cell.": "CSV/TSV tiene una celda entre comillas incompleta.",
|
|
97
|
+
"… {v0} additional rows": "… {v0} filas adicionales",
|
|
98
|
+
"── Row {v0} ──": "── Fila {v0} ──",
|
|
99
|
+
"Table too large: maximum 24 columns and 10000 rows.": "Tabla demasiado grande: máximo 24 columnas y 10000 filas.",
|
|
100
|
+
"(empty table)": "(tabla vacía)",
|
|
101
|
+
"No Markdown table found.": "No se encontró una tabla Markdown.",
|
|
102
|
+
"The JSON table requires a list of objects, or {data: [...]}.": "La tabla JSON requiere una lista de objetos, o {data: [...]}.",
|
|
103
|
+
"[image: {v0}]": "[imagen: {v0}]",
|
|
104
|
+
"That message is no longer in queue.": "Ese mensaje ya no está en cola.",
|
|
105
|
+
"The queue supports up to 1 MiB of text.": "La cola admite hasta 1 MiB de texto.",
|
|
106
|
+
"The queue supports up to 32 messages.": "La cola admite hasta 32 mensajes.",
|
|
107
|
+
"The message is empty.": "El mensaje está vacío.",
|
|
108
|
+
" Suggestions · {v0} · Tab completes": " Sugerencias · {v0} · Tab completa",
|
|
109
|
+
"Action prepared · Esc recovers your draft · Enter sends and restores it": "Acción preparada · Esc recupera tu borrador · Enter envía y lo restaura",
|
|
110
|
+
"Help open · composer active · PgUp/PgDn scrolls · Esc/F1 closes": "Ayuda abierta · composer activo · PgUp/PgDn desplaza · Esc/F1 cierra",
|
|
111
|
+
"Tab or Enter completes · ↑/↓ chooses · Esc closes · does not send": "Tab o Enter completa · ↑/↓ elige · Esc cierra · no envía",
|
|
112
|
+
" SELECTION · Drag and copy from your terminal · F5/Esc returns": " SELECCIÓN · Arrastra y copia desde tu terminal · F5/Esc vuelve",
|
|
113
|
+
" Composer · {v0} · Alt+Enter multiline · Ctrl+G editor": " Composer · {v0} · Alt+Enter multilínea · Ctrl+G editor",
|
|
114
|
+
"Enter sends": "Enter envía",
|
|
115
|
+
"Enter queues": "Enter encola",
|
|
116
|
+
" Editing queued message · Enter saves · Esc cancels": " Editando mensaje en cola · Enter guarda · Esc cancela",
|
|
117
|
+
" · history ↑": " · historial ↑",
|
|
118
|
+
" · work {v0}/{v1}{v2}": " · trabajo {v0}/{v1}{v2}",
|
|
119
|
+
"{v0} in queue{v1}": "{v0} en cola{v1}",
|
|
120
|
+
" · paused": " · pausada",
|
|
121
|
+
"● Ready · {v0} tokens": "● Listo · {v0} tokens",
|
|
122
|
+
" · cache ": " · caché ",
|
|
123
|
+
" · measured baseline": " · base medida",
|
|
124
|
+
" Running": " En ejecución",
|
|
125
|
+
" F4 manage": " F4 gestionar",
|
|
126
|
+
" Queue · {v0}{v1}": " Cola · {v0}{v1}",
|
|
127
|
+
" paused": " pausada",
|
|
128
|
+
"local model": "modelo local",
|
|
129
|
+
"vision": "visión",
|
|
130
|
+
"connecting": "conectando",
|
|
131
|
+
"Enlarge the terminal to 24×10": "Amplía el terminal a 24×10",
|
|
132
|
+
" {v0} interrupted message(s); not resent. R requeues the first.": " {v0} mensaje(s) interrumpidos; no se reenviaron. R reencola el primero.",
|
|
133
|
+
" Empty queue. Send a message while the agent works.": " Cola vacía. Envía un mensaje mientras el agente trabaja.",
|
|
134
|
+
" Running: ": " En ejecución: ",
|
|
135
|
+
" e edit · d remove · Ctrl+↑/↓ reorder · Space pause/resume · Esc return": " e editar · d quitar · Ctrl+↑/↓ ordenar · Espacio pausar/reanudar · Esc volver",
|
|
136
|
+
" Message queue · {v0}": " Cola de mensajes · {v0}",
|
|
137
|
+
"active": "activa",
|
|
138
|
+
"paused": "pausada",
|
|
139
|
+
" No matches.": " No hay coincidencias.",
|
|
140
|
+
"(empty)": "(vacía)",
|
|
141
|
+
" ↑/↓ choose · Enter resumes with history · Esc returns": " ↑/↓ elegir · Enter reanuda con historial · Esc vuelve",
|
|
142
|
+
" Search: {v0}": " Buscar: {v0}",
|
|
143
|
+
"type to filter": "escribe para filtrar",
|
|
144
|
+
" Sessions · ": " Sesiones · ",
|
|
145
|
+
" Help · composer active · type below · PgUp/PgDn scrolls · Esc/F1 closes": " Ayuda · composer activo · escribe abajo · PgUp/PgDn desplaza · Esc/F1 cierra",
|
|
146
|
+
" {v0} {v1} · Enter confirms · y/n · Esc denies": " {v0} {v1} · Enter confirma · y/n · Esc deniega",
|
|
147
|
+
" Yes": " Sí",
|
|
148
|
+
"❯ Yes": "❯ Sí",
|
|
149
|
+
" Detail {v0}/{v1} · PgUp/PgDn to review": " Detalle {v0}/{v1} · PgUp/PgDn para revisar",
|
|
150
|
+
" Allow {v0}": " Autorizar {v0}",
|
|
151
|
+
"Next: ": "Siguiente: ",
|
|
152
|
+
"Waiting: {v0}": "Espera: {v0}",
|
|
153
|
+
"TO VERIFY": "POR VERIFICAR",
|
|
154
|
+
"DONE · checks recorded": "HECHO · checks registrados",
|
|
155
|
+
"BLOCKED": "BLOQUEADO",
|
|
156
|
+
"IN PROGRESS": "EN CURSO",
|
|
157
|
+
"PENDING": "PENDIENTE",
|
|
158
|
+
" /work resume continues · wheel/PgUp/PgDn scrolls · Esc returns": " /work resume continúa · rueda/PgUp/PgDn desplaza · Esc vuelve",
|
|
159
|
+
"No plan. Ask for a task with steps and checks.": "Sin plan. Pide una tarea con pasos y comprobaciones.",
|
|
160
|
+
" Work · plan v{v0}": " Trabajo · plan v{v0}",
|
|
161
|
+
" No matches. Change section with Tab.": " Sin coincidencias. Cambia de sección con Tab.",
|
|
162
|
+
" Tab changes section · ↑/↓ chooses · Enter prepares/inspects · Esc returns": " Tab cambia sección · ↑/↓ elige · Enter prepara/inspecciona · Esc vuelve",
|
|
163
|
+
" Discover · {v0} · {v1}": " Descubrir · {v0} · {v1}",
|
|
164
|
+
"COMMANDS": "COMANDOS",
|
|
165
|
+
"TOOLS": "HERRAMIENTAS",
|
|
166
|
+
" Tab prepares a request · does not send · Esc returns": " Tab prepara una petición · no envía · Esc vuelve",
|
|
167
|
+
" Human control · Esc returns to catalog": " Control humano · Esc vuelve al catálogo",
|
|
168
|
+
" No fields declared.": " Sin campos declarados.",
|
|
169
|
+
"value": "valor",
|
|
170
|
+
" Arguments · * required": " Argumentos · * obligatorio",
|
|
171
|
+
"Declared effects: ": "Efectos declarados: ",
|
|
172
|
+
" Consulting does not authorize. Execution preserves the house's judgment.": " Consultar no autoriza. La ejecución conserva el juicio de la casa.",
|
|
173
|
+
" Audience: {v0} · source: {v1}": " Audiencia: {v0} · origen: {v1}",
|
|
174
|
+
"core/MCP": "núcleo/MCP",
|
|
175
|
+
" Esc returns; nothing was executed.": " Esc vuelve; no se ejecutó nada.",
|
|
176
|
+
" Operation removed from the catalog": " Operación retirada del catálogo",
|
|
177
|
+
"{v0} Subagent {v1} · {v2} · {v3}": "{v0} Subagente {v1} · {v2} · {v3}",
|
|
178
|
+
" You": " Tú",
|
|
179
|
+
" COMAL · on the comal one cooks": " COMAL · sobre el comal se cocina",
|
|
180
|
+
" F6 discover · F2 sessions · Shift+Tab mode": " F6 descubrir · F2 sesiones · Shift+Tab modo",
|
|
181
|
+
" Type a task; / or F6 show you what you can do.": " Escribe una tarea; / o F6 te muestran qué puedes hacer.",
|
|
182
|
+
" What are we building today?": " ¿Qué construimos hoy?",
|
|
183
|
+
"Still connecting; the draft is preserved.": "Todavía conectando; el borrador se conserva.",
|
|
184
|
+
"Use the tool {v0} to {v1}": "Usa la herramienta {v0} para {v1}",
|
|
185
|
+
"Human control: use the corresponding command or /plugins run; the model is not asked.": "Control humano: usa el comando correspondiente o /plugins run; no se pide al modelo.",
|
|
186
|
+
"That operation is no longer published. Return to the catalog.": "Esa operación ya no está publicada. Vuelve al catálogo.",
|
|
187
|
+
"Full response copied to clipboard (original Markdown).": "Respuesta completa copiada al portapapeles (Markdown original).",
|
|
188
|
+
"Text received so far copied to clipboard; the response is still arriving.": "Texto recibido hasta ahora copiado al portapapeles; la respuesta sigue llegando.",
|
|
189
|
+
"There is no response to copy yet.": "Todavía no hay una respuesta para copiar.",
|
|
190
|
+
"{v0} open tasks remain · /work resume continues from the checkpoint.": "Quedan {v0} tareas abiertas · /work resume continúa desde el checkpoint.",
|
|
191
|
+
"Work: {v0}.": "Trabajo: {v0}.",
|
|
192
|
+
"The house returned the report to mailbox {v0} · {v1}.": "La casa devolvió el informe al buzón {v0} · {v1}.",
|
|
193
|
+
"The house could not send the report: {v0}": "La casa no pudo enviar el informe: {v0}",
|
|
194
|
+
"Continues {v0} · {v1}/{v2} calls · checkpoint preserved.": "Continúa {v0} · {v1}/{v2} llamadas · checkpoint conservado.",
|
|
195
|
+
"Processing turn": "Procesando turno",
|
|
196
|
+
"Checking /goal": "Verificando /goal",
|
|
197
|
+
"Checking auto permission": "Revisando permiso auto",
|
|
198
|
+
"Waiting for the model": "Esperando al modelo",
|
|
199
|
+
"Executing {v0}": "Ejecutando {v0}",
|
|
200
|
+
"Receiving response": "Recibiendo respuesta",
|
|
201
|
+
"MCP reload not applied: {v0}": "No se aplicó la recarga MCP: {v0}",
|
|
202
|
+
"Plugin {v0} · {v1}{v2}": "Plugin {v0} · {v1}{v2}",
|
|
203
|
+
" · {v0} operations": " · {v0} operaciones",
|
|
204
|
+
"Enabling plugin {v0}": "Activando plugin {v0}",
|
|
205
|
+
"MCP updated · {v0} servers connected · {v1} tools available in packs.": "MCP actualizado · {v0} servidores conectados · {v1} herramientas disponibles en packs.",
|
|
206
|
+
"Response discarded ({v0}) · bounded retry · maximum output {v1} tokens.": "Respuesta descartada ({v0}) · reintento acotado · salida máxima {v1} tokens.",
|
|
207
|
+
"\n\n[Incomplete response discarded; its tools were not executed.]": "\n\n[Respuesta incompleta descartada; no se ejecutaron sus herramientas.]",
|
|
208
|
+
"Deriving state from the ledger": "Derivando estado del libro",
|
|
209
|
+
"Screenshot saved as a reference · {v0}.": "Captura guardada como referencia · {v0}.",
|
|
210
|
+
"the provider does not advertise vision": "el proveedor no anuncia visión",
|
|
211
|
+
"no space for pixels in the context": "sin espacio para píxeles en el contexto",
|
|
212
|
+
"Vision · {v0} images in the request · reserve ~{v1} tokens.": "Visión · {v0} imágenes en la petición · reserva ~{v1} tokens.",
|
|
213
|
+
"Model reasoning · {v0} characters": "Modelo razonando · {v0} caracteres",
|
|
214
|
+
"Preparing tools · {v0} characters": "Preparando herramientas · {v0} caracteres",
|
|
215
|
+
"Context resumed from the ledger: ~{v0} tokens. Original history preserved.": "Contexto reanudado desde el libro: ~{v0} tokens. Historial original conservado.",
|
|
216
|
+
"Ready": "Listo",
|
|
217
|
+
"Preparing turn": "Preparando turno",
|
|
218
|
+
"Connecting to the model": "Conectando con el modelo",
|
|
219
|
+
"# Comal · keyboard\n\n| Action | Key |\n| --- | --- |\n| Discover commands and tools | F6 |\n| Complete command or argument | / and Tab; ↑/↓ chooses; Esc closes |\n| Send / enqueue | Enter (suggestions only complete) |\n| New line | Alt+Enter or Ctrl+J |\n| Switch mode | Shift+Tab |\n| Edit with $VISUAL/$EDITOR | Ctrl+G |\n| Work board | F3 or /board |\n| Continue from checkpoint | /work resume |\n| Sessions / resume | F2 or Ctrl+S |\n| Message queue | F4 or Ctrl+Q |\n| Expand tool results | Ctrl+O |\n| Scroll history | Mouse wheel / PgUp / PgDn |\n| Select text with mouse | F5 or /select; F5 or Esc returns |\n| Copy last full response | Alt+C or /copy |\n| Go to end | Ctrl+End |\n| Composer history | ↑/↓ on first/last line |\n| Start / end of line | Home / End or Ctrl+A / Ctrl+E |\n| Delete word / start / end | Ctrl+W / Ctrl+U / Ctrl+K |\n| Cancel turn / pause goal | Ctrl+C or Esc while working |\n| Exit | Ctrl+D with empty composer or /exit |\n\nYou can keep typing during streaming. Sent messages run in order when the current turn ends. F4 lets you pause, edit, remove, and reorder the queue.\n\n/context shows the context budget. /compact derives state without LLM and waits its turn if there is active work. /state shows request and state; /audit verifies the ledger. The original history is preserved.\n/house shows execution receipts and interrupted operations; /operations shows contracts and declared effects. Execution success does not certify the human goal.\n/plugins shows plugins, dependencies, and operations; enable/disable/reload wait their turn and only the user can change their lifecycle.\n/model ID and /effort LEVEL change the next call; during work they stay queued. /models lists models and /capabilities shows support, provenance, and levels. E indicates effort; Ctx shows the effective window (k/M tokens); ~ indicates a configured limit, ? unknown/fallback capability. /context shows exact values, reserve, and source. /login PROVIDER authenticates; /logout PROVIDER removes credentials from Comal.\n\n**Help keeps the composer active.** You can type while viewing it; PgUp/PgDn scrolls it and Esc/F1 closes it. Sending returns to the conversation. The wheel scrolls the conversation; the arrows edit the draft. F5 releases the mouse and freezes the view: drag to select and use your terminal's copy action (usually Ctrl+Shift+C). Work continues and the view updates when you return. /copy preserves Markdown and code blocks, without the screen line wrapping. Multi-line paste is not sent automatically.": "# Comal · teclado\n\n| Acción | Tecla |\n| --- | --- |\n| Descubrir comandos y herramientas | F6 |\n| Completar comando o argumento | / y Tab; ↑/↓ elige; Esc cierra |\n| Enviar / encolar | Enter (las sugerencias sólo completan) |\n| Nueva línea | Alt+Enter o Ctrl+J |\n| Cambiar modo | Shift+Tab |\n| Editar con $VISUAL/$EDITOR | Ctrl+G |\n| Tablero de trabajo | F3 o /board |\n| Continuar desde checkpoint | /work resume |\n| Sesiones / reanudar | F2 o Ctrl+S |\n| Cola de mensajes | F4 o Ctrl+Q |\n| Desplegar resultados de herramientas | Ctrl+O |\n| Scroll del historial | Rueda del mouse / PgUp / PgDn |\n| Seleccionar texto con el mouse | F5 o /select; F5 o Esc vuelve |\n| Copiar última respuesta completa | Alt+C o /copy |\n| Ir al final | Ctrl+End |\n| Historial del composer | ↑/↓ en primera/última línea |\n| Inicio / final de línea | Home / End o Ctrl+A / Ctrl+E |\n| Borrar palabra / inicio / final | Ctrl+W / Ctrl+U / Ctrl+K |\n| Cancelar turno / pausar goal | Ctrl+C o Esc mientras trabaja |\n| Salir | Ctrl+D con composer vacío o /exit |\n\nPuedes seguir escribiendo durante el streaming. Los mensajes enviados se ejecutan en orden al terminar el turno actual. F4 permite pausar, editar, quitar y reordenar la cola.\n\n/context muestra el presupuesto de contexto. /compact deriva el estado sin LLM y espera su turno si hay trabajo activo. /state muestra petición y estado; /audit verifica el libro. El historial original se conserva.\n/house muestra recibos de ejecución y operaciones interrumpidas; /operations muestra contratos y efectos declarados. El éxito de ejecución no certifica el objetivo humano.\n/plugins muestra plugins, dependencias y operaciones; enable/disable/reload esperan su turno y sólo el usuario puede cambiar su ciclo de vida.\n/model ID y /effort NIVEL cambian la siguiente llamada; durante trabajo quedan en cola. /models lista modelos y /capabilities muestra soporte, procedencia y niveles. E indica esfuerzo; Ctx muestra la ventana efectiva (k/M tokens); ~ indica un límite configurado, ? capacidad desconocida/fallback. /context muestra valores exactos, reserva y fuente. /login PROVEEDOR autentica; /logout PROVEEDOR retira credenciales de Comal.\n\n**La ayuda mantiene activo el composer.** Puedes escribir mientras la consultas; PgUp/PgDn la desplaza y Esc/F1 la cierra. Al enviar vuelve la conversación. La rueda desplaza la conversación; las flechas editan el borrador. F5 libera el mouse y congela la vista: arrastra para seleccionar y usa la acción de copiar de tu terminal (normalmente Ctrl+Shift+C). El trabajo continúa y la vista se actualiza al volver. /copy conserva Markdown y bloques de código, sin el ajuste de líneas de la pantalla. El pegado multilínea no se envía automáticamente.",
|
|
220
|
+
"View or configure MCP servers": "Ver o configurar servidores MCP",
|
|
221
|
+
"View plugins and control their lifecycle": "Ver plugins y controlar su ciclo de vida",
|
|
222
|
+
"View and control child runs": "Ver y controlar ejecuciones hijas",
|
|
223
|
+
"Remove Comal credentials": "Retirar credenciales de Comal",
|
|
224
|
+
"State derived · ~{v0} tokens · 0 LLM summary calls · evidence saved.": "Estado derivado · ~{v0} tokens · 0 llamadas de resumen al LLM · evidencia guardada.",
|
|
225
|
+
"Derive ledger state without an LLM": "Derivar el estado del libro sin LLM",
|
|
226
|
+
"Verify ledger integrity": "Verificar la integridad del libro"
|
|
227
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {AsyncLocalStorage} from 'node:async_hooks';
|
|
2
|
+
import spanish from './es.mjs';
|
|
3
|
+
|
|
4
|
+
export const SUPPORTED_LANGUAGES=Object.freeze(['en','es']);
|
|
5
|
+
const context=new AsyncLocalStorage();
|
|
6
|
+
function normalized(value){return String(value||'').trim().split(':')[0].split('.')[0].split('@')[0].replaceAll('_','-');}
|
|
7
|
+
export function detectLanguage(env=process.env,intlLocale=Intl.DateTimeFormat().resolvedOptions().locale){
|
|
8
|
+
const raw=env.LC_ALL||env.LC_MESSAGES||env.LANGUAGE||env.LANG||intlLocale||'en';
|
|
9
|
+
const detected=normalized(raw),base=detected.split('-')[0].toLowerCase();
|
|
10
|
+
return Object.freeze({detected:detected||'en',language:SUPPORTED_LANGUAGES.includes(base)?base:'en',source:env.LC_ALL?'LC_ALL':env.LC_MESSAGES?'LC_MESSAGES':env.LANGUAGE?'LANGUAGE':env.LANG?'LANG':'Intl'});
|
|
11
|
+
}
|
|
12
|
+
export function resolveLanguage({requested,env=process.env}={}){
|
|
13
|
+
const detected=detectLanguage(env),selection=requested??(env.COMAL_LANG?.trim()||'en');
|
|
14
|
+
const base=normalized(selection).split('-')[0].toLowerCase();
|
|
15
|
+
if(![...SUPPORTED_LANGUAGES,'auto'].includes(base))throw Object.assign(new Error('Unsupported UI language. Use en, es or auto.'),{code:'UI_LANGUAGE_UNSUPPORTED'});
|
|
16
|
+
return Object.freeze({requested:base,language:base==='auto'?detected.language:base,detected:detected.detected,detectedSource:detected.source,source:requested!==undefined?'flag':env.COMAL_LANG!==undefined?'COMAL_LANG':'default',defaultLanguage:'en'});
|
|
17
|
+
}
|
|
18
|
+
// Feature catalogs register English IDs without importing the interface into the model.
|
|
19
|
+
const messages = new Map(Object.entries(spanish));
|
|
20
|
+
export function registerMessages(catalog) {
|
|
21
|
+
if (!catalog || typeof catalog !== 'object' || Array.isArray(catalog)) throw new TypeError('A message catalog must be an object.');
|
|
22
|
+
for (const [key, value] of Object.entries(catalog)) {
|
|
23
|
+
if (typeof value !== 'string') throw new TypeError('Message translations must be strings.');
|
|
24
|
+
if (!messages.has(key)) messages.set(key, value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Entry points validate environment/flags inside their error boundary. Module imports stay safe.
|
|
28
|
+
let configured=resolveLanguage({env:{}});
|
|
29
|
+
export function configureLanguage(options={}){configured=resolveLanguage(options);return configured;}
|
|
30
|
+
export function languageInfo(){return context.getStore()||configured;}
|
|
31
|
+
export function withLanguage(selection,fn){return context.run(resolveLanguage(typeof selection==='string'?{requested:selection}:selection),fn);}
|
|
32
|
+
/** English message IDs are the base. Only host-authored messages call this function.
|
|
33
|
+
* Never run translations over user text, model output, contracts or recorded evidence.
|
|
34
|
+
*/
|
|
35
|
+
export function t(message,values={}){
|
|
36
|
+
const text=languageInfo().language==='es'?(messages.get(message)??message):message;
|
|
37
|
+
return String(text).replace(/\{([A-Za-z][A-Za-z0-9_]*)\}/g,(whole,key)=>Object.hasOwn(values,key)?String(values[key]):whole);
|
|
38
|
+
}
|
|
39
|
+
export function formatNumber(value,options={}){return new Intl.NumberFormat(languageInfo().language,options).format(value);}
|
|
40
|
+
export function languageArguments(args){
|
|
41
|
+
let requested;const remaining=[];
|
|
42
|
+
for(let i=0;i<args.length;i++){
|
|
43
|
+
const item=args[i];if(item==='--'){remaining.push(...args.slice(i));break;}
|
|
44
|
+
if(item==='--lang'){if(!args[i+1]||args[i+1].startsWith('--'))throw Object.assign(new Error('--lang requires en, es or auto.'),{code:'UI_LANGUAGE_REQUIRED'});requested=args[++i];}
|
|
45
|
+
else if(item.startsWith('--lang='))requested=item.slice(7);else remaining.push(item);
|
|
46
|
+
}
|
|
47
|
+
return {requested,remaining};
|
|
48
|
+
}
|