@inetafrica/open-claudia 2.14.8 → 3.0.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/.env.example +30 -4
- package/CHANGELOG.md +21 -0
- package/README.md +87 -66
- package/bin/agent.js +44 -18
- package/bin/cli.js +30 -28
- package/bin/dream.js +5 -11
- package/bin/loopback-client.js +29 -5
- package/bin/schedule.js +19 -5
- package/bin/tool.js +23 -5
- package/bot-agent.js +5 -2350
- package/bot.js +105 -7
- package/channels/telegram/adapter.js +6 -1
- package/channels/voice/adapter.js +1 -0
- package/channels/voice/manage.js +43 -5
- package/config-dir.js +3 -11
- package/core/actions.js +149 -40
- package/core/approvals.js +136 -29
- package/core/auth-flow.js +103 -19
- package/core/config-dir.js +19 -0
- package/core/config.js +127 -69
- package/core/doctor.js +111 -57
- package/core/dream.js +219 -62
- package/core/enforcer.js +0 -0
- package/core/entities.js +2 -1
- package/core/fsutil.js +55 -0
- package/core/handlers.js +503 -217
- package/core/ideas.js +2 -1
- package/core/identity.js +8 -11
- package/core/io.js +24 -3
- package/core/jobs.js +589 -72
- package/core/lessons.js +3 -2
- package/core/loopback.js +45 -8
- package/core/memory-mutation-queue.js +241 -0
- package/core/migration-backup.js +1060 -0
- package/core/pack-review.js +295 -88
- package/core/packs.js +4 -3
- package/core/persona.js +2 -1
- package/core/process-tree.js +19 -2
- package/core/provider-migration.js +39 -0
- package/core/provider-status.js +80 -0
- package/core/providers/claude-events.js +117 -0
- package/core/providers/claude-hook.js +114 -0
- package/core/providers/claude.js +296 -0
- package/core/providers/codex-events.js +121 -0
- package/core/providers/codex-hook.js +280 -0
- package/core/providers/codex.js +286 -0
- package/core/providers/contract.js +153 -0
- package/core/providers/env.js +148 -0
- package/core/providers/events.js +170 -0
- package/core/providers/hook-command.js +22 -0
- package/core/providers/index.js +240 -0
- package/core/providers/utility-policy.js +269 -0
- package/core/recall/classic.js +2 -2
- package/core/recall/discoverer.js +71 -22
- package/core/recall/metrics.js +27 -1
- package/core/recall/tuning.js +4 -1
- package/core/recall/warm-walker.js +151 -108
- package/core/recall-filter.js +86 -61
- package/core/router.js +79 -7
- package/core/run-context.js +185 -0
- package/core/runner.js +1415 -1282
- package/core/scheduler.js +515 -210
- package/core/side-chat.js +346 -0
- package/core/state.js +1096 -95
- package/core/subagent.js +72 -98
- package/core/system-prompt.js +462 -279
- package/core/tool-guard.js +73 -39
- package/core/tools.js +22 -5
- package/core/transcripts.js +30 -1
- package/core/usage-log.js +19 -4
- package/core/utility-agent.js +654 -0
- package/core/web-auth.js +29 -13
- package/core/web-sessions.js +78 -0
- package/docs/CHANNEL_DESIGN.md +181 -0
- package/docs/MULTI_CHANNEL_PLAN.md +254 -0
- package/docs/PROVIDER_MIGRATION.md +67 -0
- package/health.js +50 -39
- package/package.json +51 -4
- package/setup.js +198 -38
- package/soul.md +39 -0
- package/test-ability-extraction.js +5 -0
- package/test-approval-async.js +63 -4
- package/test-cursor-removal.js +334 -0
- package/test-delivery-contract.js +88 -0
- package/test-enforcer.js +70 -27
- package/test-fixtures/current-provider-parity.json +132 -0
- package/test-fixtures/fake-agent-cli.js +477 -0
- package/test-fixtures/migrations/claude-only/jobs.json +3 -0
- package/test-fixtures/migrations/claude-only/sessions.json +5 -0
- package/test-fixtures/migrations/claude-only/state.json +5 -0
- package/test-fixtures/migrations/cursor-selected/crons.json +3 -0
- package/test-fixtures/migrations/cursor-selected/jobs.json +3 -0
- package/test-fixtures/migrations/cursor-selected/sessions.json +5 -0
- package/test-fixtures/migrations/cursor-selected/state.json +6 -0
- package/test-fixtures/migrations/dual-provider/jobs.json +3 -0
- package/test-fixtures/migrations/dual-provider/sessions.json +7 -0
- package/test-fixtures/migrations/dual-provider/state.json +10 -0
- package/test-fixtures/migrations/malformed-partial/jobs.json +1 -0
- package/test-fixtures/migrations/malformed-partial/sessions.json +1 -0
- package/test-fixtures/migrations/malformed-partial/sessions.json.bak +3 -0
- package/test-fixtures/migrations/malformed-partial/state.json +1 -0
- package/test-fixtures/migrations/malformed-partial/state.json.bak +5 -0
- package/test-fixtures/migrations/missing-project/jobs.json +3 -0
- package/test-fixtures/migrations/missing-project/sessions.json +3 -0
- package/test-fixtures/migrations/missing-project/state.json +4 -0
- package/test-fixtures/migrations/multi-user/jobs.json +4 -0
- package/test-fixtures/migrations/multi-user/sessions.json +4 -0
- package/test-fixtures/migrations/multi-user/state.json +6 -0
- package/test-fixtures/provider-event-samples.json +17 -0
- package/test-learning-e2e.js +5 -0
- package/test-memory-mutation-queue.js +191 -0
- package/test-provider-boot-matrix.js +399 -0
- package/test-provider-bootstrap.js +158 -0
- package/test-provider-capabilities.js +232 -0
- package/test-provider-claude.js +206 -0
- package/test-provider-codex.js +228 -0
- package/test-provider-compaction.js +371 -0
- package/test-provider-core-prompt.js +264 -0
- package/test-provider-coupling-audit.js +90 -0
- package/test-provider-dream.js +312 -0
- package/test-provider-enforcer.js +252 -0
- package/test-provider-env-isolation.js +150 -0
- package/test-provider-events.js +141 -0
- package/test-provider-fixture.js +332 -0
- package/test-provider-gateway.js +508 -0
- package/test-provider-language.js +89 -0
- package/test-provider-migration-backup.js +424 -0
- package/test-provider-pack-review.js +436 -0
- package/test-provider-parity-e2e.js +537 -0
- package/test-provider-prompt-parity.js +89 -0
- package/test-provider-recall.js +271 -0
- package/test-provider-registry.js +251 -0
- package/test-provider-resume-parity.js +41 -0
- package/test-provider-run-lifecycle.js +349 -0
- package/test-provider-runner.js +271 -0
- package/test-provider-scheduler.js +689 -0
- package/test-provider-session-commands.js +185 -0
- package/test-provider-session-history.js +205 -0
- package/test-provider-side-chat.js +337 -0
- package/test-provider-state-migration.js +316 -0
- package/test-provider-stream-decoder.js +69 -0
- package/test-provider-subagent.js +228 -0
- package/test-provider-tool-hooks.js +360 -0
- package/test-recall-discoverer.js +1 -0
- package/test-recall-engine.js +3 -3
- package/test-recall-evolution.js +18 -0
- package/test-run-lock.js +63 -0
- package/test-runner-watchdog-static.js +16 -8
- package/test-single-runtime.js +35 -0
- package/test-telegram-poll-recovery.js +56 -0
- package/test-tool-guard.js +56 -0
- package/test-tools.js +19 -0
- package/test-unified-identity.js +3 -1
- package/test-usage-accounting.js +92 -20
- package/test-utility-provider-policy.js +486 -0
- package/test-web-sessions.js +74 -0
- package/web.js +159 -45
package/bin/cli.js
CHANGED
|
@@ -11,22 +11,17 @@ const botDir = path.join(__dirname, "..");
|
|
|
11
11
|
const configDir = require(path.join(botDir, "config-dir"));
|
|
12
12
|
const health = require(path.join(botDir, "health"));
|
|
13
13
|
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
const mode = fs.readFileSync(modeFile, "utf-8").trim();
|
|
18
|
-
if (mode === "agent") return "bot-agent.js";
|
|
19
|
-
} catch (e) {}
|
|
20
|
-
return "bot.js";
|
|
14
|
+
function getBotMode() {
|
|
15
|
+
return require(path.join(botDir, "core", "side-chat")).readRuntimeMode(configDir);
|
|
21
16
|
}
|
|
22
17
|
|
|
23
|
-
function startBot(
|
|
18
|
+
function startBot(args) {
|
|
24
19
|
// Start web UI alongside bot if WEB_UI=true or --web flag
|
|
25
20
|
if (process.env.WEB_UI === "true" || args.includes("--web")) {
|
|
26
21
|
const { startWebServer } = require(path.join(botDir, "web.js"));
|
|
27
22
|
startWebServer();
|
|
28
23
|
}
|
|
29
|
-
require(path.join(botDir,
|
|
24
|
+
require(path.join(botDir, "bot.js"));
|
|
30
25
|
}
|
|
31
26
|
|
|
32
27
|
function sendViaLoopback(kind, restArgs) {
|
|
@@ -52,7 +47,7 @@ function sendViaLoopback(kind, restArgs) {
|
|
|
52
47
|
if (info) { sendUrl = info.url; sendToken = info.token; }
|
|
53
48
|
}
|
|
54
49
|
if (!sendUrl || !sendToken || !channelId || !adapterId) {
|
|
55
|
-
console.error("No active chat context. This command only works from inside
|
|
50
|
+
console.error("No active chat context. This command only works from inside an Open Claudia agent task spawned by the bot.");
|
|
56
51
|
process.exit(2);
|
|
57
52
|
}
|
|
58
53
|
|
|
@@ -132,13 +127,17 @@ function findBotProcesses() {
|
|
|
132
127
|
|
|
133
128
|
switch (command) {
|
|
134
129
|
case "setup":
|
|
135
|
-
require(path.join(botDir, "setup.js"))
|
|
130
|
+
require(path.join(botDir, "setup.js")).main().catch((error) => {
|
|
131
|
+
console.error(error.stack || error.message);
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
});
|
|
136
134
|
break;
|
|
137
135
|
|
|
138
|
-
// PreToolUse
|
|
139
|
-
// payload on stdin; exit 2 + stderr
|
|
140
|
-
//
|
|
141
|
-
//
|
|
136
|
+
// Provider-neutral PreToolUse target. Claude and Codex both pipe the Bash
|
|
137
|
+
// payload on stdin; exit 2 + stderr denies the command and exit 0 allows it.
|
|
138
|
+
// Transport initialization is mandatory in the provider adapters. Payload
|
|
139
|
+
// compatibility remains fail-open so an unknown future event shape cannot
|
|
140
|
+
// brick every shell command.
|
|
142
141
|
case "deny-gate-hook": {
|
|
143
142
|
let code = 0;
|
|
144
143
|
try {
|
|
@@ -172,7 +171,7 @@ switch (command) {
|
|
|
172
171
|
|
|
173
172
|
case "start": {
|
|
174
173
|
const skipHealthCheck = args.includes("--skip-health") || args.includes("--force");
|
|
175
|
-
const
|
|
174
|
+
const botMode = getBotMode();
|
|
176
175
|
|
|
177
176
|
// Run health checks before starting (unless skipped)
|
|
178
177
|
if (!skipHealthCheck) {
|
|
@@ -187,15 +186,15 @@ switch (command) {
|
|
|
187
186
|
console.log("Warnings:");
|
|
188
187
|
for (const w of results.warnings) console.log(` - ${w}`);
|
|
189
188
|
}
|
|
190
|
-
console.log(`Starting Open Claudia (${
|
|
191
|
-
startBot(
|
|
189
|
+
console.log(`Starting Open Claudia (${botMode} mode, modular runtime)...`);
|
|
190
|
+
startBot(args);
|
|
192
191
|
}).catch((err) => {
|
|
193
192
|
console.error("Health check failed:", err.message);
|
|
194
193
|
process.exit(1);
|
|
195
194
|
});
|
|
196
195
|
} else {
|
|
197
|
-
console.log(`Starting Open Claudia (${
|
|
198
|
-
startBot(
|
|
196
|
+
console.log(`Starting Open Claudia (${botMode} mode, modular runtime)...`);
|
|
197
|
+
startBot(args);
|
|
199
198
|
}
|
|
200
199
|
break;
|
|
201
200
|
}
|
|
@@ -215,9 +214,9 @@ switch (command) {
|
|
|
215
214
|
const { startWebServer: startWeb, isConfigured } = require(path.join(botDir, "web.js"));
|
|
216
215
|
startWeb();
|
|
217
216
|
if (isConfigured()) {
|
|
218
|
-
const
|
|
219
|
-
console.log(`Bot config found. Starting bot (${
|
|
220
|
-
require(path.join(botDir,
|
|
217
|
+
const botMode = getBotMode();
|
|
218
|
+
console.log(`Bot config found. Starting bot (${botMode} mode, modular runtime)...`);
|
|
219
|
+
require(path.join(botDir, "bot.js"));
|
|
221
220
|
} else {
|
|
222
221
|
console.log("No config found. Complete setup in the web UI.");
|
|
223
222
|
}
|
|
@@ -232,7 +231,10 @@ switch (command) {
|
|
|
232
231
|
}
|
|
233
232
|
// Legacy: open-claudia auth (no subcommand) → setup.js auth mode
|
|
234
233
|
process.argv = [process.argv[0], process.argv[1], "--auth"];
|
|
235
|
-
require(path.join(botDir, "setup.js"))
|
|
234
|
+
require(path.join(botDir, "setup.js")).main().catch((error) => {
|
|
235
|
+
console.error(error.stack || error.message);
|
|
236
|
+
process.exitCode = 1;
|
|
237
|
+
});
|
|
236
238
|
break;
|
|
237
239
|
}
|
|
238
240
|
|
|
@@ -419,12 +421,12 @@ Memory tools:
|
|
|
419
421
|
open-claudia dream [--dry-run] Run the memory consolidation pass now
|
|
420
422
|
|
|
421
423
|
Background work (only inside an active bot-spawned task):
|
|
422
|
-
open-claudia schedule-wakeup <when> "<prompt>" One-shot
|
|
423
|
-
open-claudia cron-add "<sched>" "<prompt>" Recurring
|
|
424
|
+
open-claudia schedule-wakeup <when> "<prompt>" One-shot; resumes the captured selected provider session
|
|
425
|
+
open-claudia cron-add "<sched>" "<prompt>" Recurring job pinned to the selected provider session
|
|
424
426
|
open-claudia cron-list List all wakeups + crons on this channel
|
|
425
427
|
open-claudia cron-remove <id> Remove a scheduled job
|
|
426
428
|
open-claudia task add|list|start|done|remove Per-channel todo (survives restarts)
|
|
427
|
-
open-claudia agent "<prompt>" [--role X]
|
|
429
|
+
open-claudia agent "<prompt>" [--role X] [--provider active|claude|codex] Spawn a provider-isolated sub-agent
|
|
428
430
|
|
|
429
431
|
Cluster control (self-management of this bot's own deployment via the broker):
|
|
430
432
|
open-claudia cluster status|logs|restart|start|stop|scale <0|1>|sync
|
|
@@ -438,7 +440,7 @@ Multi-user / cross-channel:
|
|
|
438
440
|
|
|
439
441
|
Start options:
|
|
440
442
|
--web Also start the web UI
|
|
441
|
-
--quick Skip slow
|
|
443
|
+
--quick Skip slow provider auth and channel API checks
|
|
442
444
|
--skip-health Bypass all health checks (not recommended)
|
|
443
445
|
|
|
444
446
|
Health options:
|
package/bin/dream.js
CHANGED
|
@@ -10,8 +10,8 @@ Run the dream memory-consolidation pass now.
|
|
|
10
10
|
|
|
11
11
|
open-claudia dream [--dry-run]
|
|
12
12
|
|
|
13
|
-
Reviews all context packs and entities with
|
|
14
|
-
|
|
13
|
+
Reviews all context packs and entities with the configured high-tier utility
|
|
14
|
+
provider: merges duplicate packs, builds umbrella/parent trees,
|
|
15
15
|
tightens descriptions and tags, dedupes entities, cross-links notes, and
|
|
16
16
|
may gently evolve the persona. Merged-away packs/entities are backed up
|
|
17
17
|
under ~/.open-claudia/backup/dream-<stamp>/ first.
|
|
@@ -24,17 +24,11 @@ async function run(args) {
|
|
|
24
24
|
const dream = require(path.join(__dirname, "..", "core", "dream"));
|
|
25
25
|
|
|
26
26
|
if (args.includes("--dry-run")) {
|
|
27
|
-
const { spawnSubagent } = require(path.join(__dirname, "..", "core", "subagent"));
|
|
28
27
|
console.error("Dreaming (dry run)… this can take a minute or two.");
|
|
29
28
|
try {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
systemPrompt: "You are a background memory consolidation process. Reply with ONLY the requested JSON object. No prose, no markdown, no tool use.",
|
|
34
|
-
});
|
|
35
|
-
const decision = dream.parseDream(text);
|
|
36
|
-
if (!decision) { console.error("Unparseable model output:"); console.log(text); process.exit(1); }
|
|
37
|
-
console.log(JSON.stringify(decision, null, 2));
|
|
29
|
+
const preview = await dream.previewDream();
|
|
30
|
+
console.error(`Dream provider: ${preview.provider || "unknown"}/${preview.model || "default"}`);
|
|
31
|
+
console.log(JSON.stringify(preview.decision, null, 2));
|
|
38
32
|
process.exit(0);
|
|
39
33
|
} catch (e) { console.error(`dream failed: ${e.message}`); process.exit(1); }
|
|
40
34
|
}
|
package/bin/loopback-client.js
CHANGED
|
@@ -31,6 +31,11 @@ function context() {
|
|
|
31
31
|
const info = readLoopbackInfo();
|
|
32
32
|
if (info) { sendUrl = info.url; sendToken = info.token; }
|
|
33
33
|
}
|
|
34
|
+
let providerSettings = {};
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(process.env.OC_PROVIDER_SETTINGS || "{}");
|
|
37
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) providerSettings = parsed;
|
|
38
|
+
} catch (_) {}
|
|
34
39
|
return {
|
|
35
40
|
sendUrl,
|
|
36
41
|
sendToken,
|
|
@@ -38,8 +43,16 @@ function context() {
|
|
|
38
43
|
adapterId: process.env.OC_CHANNEL_ADAPTER,
|
|
39
44
|
userId: process.env.OC_CHANNEL_USER_ID || null,
|
|
40
45
|
canonicalUserId: process.env.OC_CANONICAL_USER_ID || null,
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
provider: process.env.OC_PROVIDER || null,
|
|
47
|
+
providerSettings,
|
|
48
|
+
project: process.env.OC_PROJECT || null,
|
|
49
|
+
projectDir: process.env.OC_PROJECT_DIR || null,
|
|
50
|
+
runId: process.env.OC_RUN_ID || null,
|
|
51
|
+
sessionId: process.env.OC_SESSION_ID || process.env.OC_LAST_SESSION_ID || null,
|
|
52
|
+
previousSessionId: process.env.OC_PREVIOUS_SESSION_ID || null,
|
|
53
|
+
occurrenceId: process.env.OC_OCCURRENCE_ID || null,
|
|
54
|
+
scheduledJobId: process.env.OC_SCHEDULED_JOB_ID || null,
|
|
55
|
+
originRunId: process.env.OC_ORIGIN_RUN_ID || null,
|
|
43
56
|
};
|
|
44
57
|
}
|
|
45
58
|
|
|
@@ -55,14 +68,25 @@ function requireContext() {
|
|
|
55
68
|
function postJson(kind, body) {
|
|
56
69
|
const ctx = requireContext();
|
|
57
70
|
const u = new URL(ctx.sendUrl);
|
|
71
|
+
const requestedProject = body && body.project ? String(body.project) : ctx.project;
|
|
72
|
+
const sameProject = !ctx.project || !requestedProject || requestedProject === ctx.project;
|
|
58
73
|
const payload = JSON.stringify({
|
|
74
|
+
...body,
|
|
59
75
|
channelId: ctx.channelId,
|
|
60
76
|
adapter: ctx.adapterId,
|
|
61
77
|
userId: ctx.userId,
|
|
62
78
|
canonicalUserId: ctx.canonicalUserId,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
79
|
+
provider: ctx.provider,
|
|
80
|
+
providerSettings: ctx.providerSettings,
|
|
81
|
+
project: requestedProject,
|
|
82
|
+
projectDir: sameProject ? ctx.projectDir : null,
|
|
83
|
+
originProject: ctx.project,
|
|
84
|
+
originProjectDir: ctx.projectDir,
|
|
85
|
+
originRunId: ctx.runId,
|
|
86
|
+
sessionId: sameProject ? ctx.sessionId : null,
|
|
87
|
+
previousSessionId: sameProject ? ctx.previousSessionId : null,
|
|
88
|
+
parentOccurrenceId: ctx.occurrenceId,
|
|
89
|
+
parentScheduledJobId: ctx.scheduledJobId,
|
|
66
90
|
});
|
|
67
91
|
return new Promise((resolve, reject) => {
|
|
68
92
|
const req = http.request({
|
package/bin/schedule.js
CHANGED
|
@@ -15,8 +15,9 @@ Crons (recurring):
|
|
|
15
15
|
open-claudia cron-list # all wakeups + crons for this channel
|
|
16
16
|
open-claudia cron-remove <id> # remove by id from cron-list
|
|
17
17
|
|
|
18
|
-
Each fire wakes the agent
|
|
19
|
-
|
|
18
|
+
Each fire wakes the agent in the same channel and resumes the selected provider
|
|
19
|
+
session captured when the schedule was created. Provider/project switches made
|
|
20
|
+
later do not retarget already-scheduled work.
|
|
20
21
|
`;
|
|
21
22
|
|
|
22
23
|
const DUR = /^(\d+)\s*(s|sec|secs|m|min|mins|h|hr|hrs|d|day|days)$/i;
|
|
@@ -68,7 +69,9 @@ async function runScheduleWakeup(args) {
|
|
|
68
69
|
const prompt = rest.slice(1).join(" ");
|
|
69
70
|
if (!prompt) { console.error("Missing prompt."); process.exit(2); }
|
|
70
71
|
try {
|
|
71
|
-
const
|
|
72
|
+
const request = { fireAt: when, prompt, label: label || prompt.slice(0, 60) };
|
|
73
|
+
if (project) request.project = project;
|
|
74
|
+
const res = await postJson("schedule-wakeup", request);
|
|
72
75
|
console.log(`Scheduled wakeup ${res.job.id} for ${new Date(when).toISOString()}.`);
|
|
73
76
|
process.exit(0);
|
|
74
77
|
} catch (e) { console.error(e.message); process.exit(1); }
|
|
@@ -83,7 +86,9 @@ async function runCronAdd(args) {
|
|
|
83
86
|
const prompt = rest.slice(1).join(" ");
|
|
84
87
|
if (!schedule || !prompt) { console.error("Usage: cron-add \"<schedule>\" \"<prompt>\""); process.exit(2); }
|
|
85
88
|
try {
|
|
86
|
-
const
|
|
89
|
+
const request = { schedule, prompt, label: label || prompt.slice(0, 60) };
|
|
90
|
+
if (project) request.project = project;
|
|
91
|
+
const res = await postJson("cron-add", request);
|
|
87
92
|
console.log(`Added cron ${res.job.id} (${schedule}).`);
|
|
88
93
|
process.exit(0);
|
|
89
94
|
} catch (e) { console.error(e.message); process.exit(1); }
|
|
@@ -93,8 +98,17 @@ async function runCronList() {
|
|
|
93
98
|
try {
|
|
94
99
|
const res = await postJson("job-list", {});
|
|
95
100
|
const list = res.jobs || [];
|
|
96
|
-
|
|
101
|
+
const archived = res.archived || [];
|
|
102
|
+
if (list.length === 0 && archived.length === 0) { console.log("No jobs."); process.exit(0); }
|
|
97
103
|
for (const j of list) console.log(fmtJob(j));
|
|
104
|
+
if (archived.length) {
|
|
105
|
+
if (list.length) console.log("");
|
|
106
|
+
console.log("Archived (disabled by provider migration — will not fire):");
|
|
107
|
+
for (const j of archived) {
|
|
108
|
+
const label = j.label || (j.prompt ? String(j.prompt).slice(0, 50) : "(no label)");
|
|
109
|
+
console.log(`${j.id} ${j.kind || "job"} ${label} — ${j.archiveReason || "archived"}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
98
112
|
process.exit(0);
|
|
99
113
|
} catch (e) { console.error(e.message); process.exit(1); }
|
|
100
114
|
}
|
package/bin/tool.js
CHANGED
|
@@ -371,7 +371,8 @@ async function run(args) {
|
|
|
371
371
|
// calling this tool) gate on the tier the OUTERMOST invocation approved
|
|
372
372
|
// instead — argv flags can't fabricate approval.
|
|
373
373
|
const gate = tools.runGate(t, toolArgs, process.env);
|
|
374
|
-
const
|
|
374
|
+
const approvals = require("../core/approvals");
|
|
375
|
+
const commandLine = approvals.formatApprovedCommand(["open-claudia", "tool", "run", t.name, ...toolArgs]);
|
|
375
376
|
let approvedVia = "";
|
|
376
377
|
|
|
377
378
|
// ── Phase 3.5: per-speaker external gate ──────────────────────────────
|
|
@@ -403,6 +404,20 @@ async function run(args) {
|
|
|
403
404
|
} else {
|
|
404
405
|
const esc = await enforcer.escalate({
|
|
405
406
|
speaker, kind: "action", command: commandLine, tier: gate.tier, reason: vet.reason,
|
|
407
|
+
canonicalUserId: process.env.OC_CANONICAL_USER_ID || "",
|
|
408
|
+
userId: process.env.OC_CHANNEL_USER_ID || "",
|
|
409
|
+
provider: process.env.OC_PROVIDER || "",
|
|
410
|
+
providerSettings: (() => {
|
|
411
|
+
try {
|
|
412
|
+
const value = JSON.parse(process.env.OC_PROVIDER_SETTINGS || "{}");
|
|
413
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
414
|
+
} catch (_) { return {}; }
|
|
415
|
+
})(),
|
|
416
|
+
project: process.env.OC_PROJECT || "",
|
|
417
|
+
projectDir: process.env.OC_PROJECT_DIR || "",
|
|
418
|
+
sessionId: process.env.OC_SESSION_ID || process.env.OC_LAST_SESSION_ID || "",
|
|
419
|
+
previousSessionId: process.env.OC_PREVIOUS_SESSION_ID || "",
|
|
420
|
+
originRunId: process.env.OC_RUN_ID || process.env.OC_ORIGIN_RUN_ID || "",
|
|
406
421
|
});
|
|
407
422
|
if (esc.escalated) {
|
|
408
423
|
console.error(`🛡️ Held for owner approval — ${speaker.name || "the external contact"} is outside mandate for this ${gate.tier} action.`);
|
|
@@ -416,7 +431,6 @@ async function run(args) {
|
|
|
416
431
|
}
|
|
417
432
|
|
|
418
433
|
if (approvalId) {
|
|
419
|
-
const approvals = require("../core/approvals");
|
|
420
434
|
if (String(process.env.OPEN_CLAUDIA_TOOL_STACK || "").trim()) {
|
|
421
435
|
console.error("⛔ --approval tokens are only honored on top-level runs, not nested tool calls.");
|
|
422
436
|
process.exitCode = 3; return;
|
|
@@ -436,7 +450,7 @@ async function run(args) {
|
|
|
436
450
|
// wakeup carrying a one-shot --approval token. Anything but an explicit
|
|
437
451
|
// approve = the command never runs.
|
|
438
452
|
const canAsk = gate.tier === "destructive" && !gate.nested && !gate.undeclared
|
|
439
|
-
&& process.env.OC_SEND_URL && process.env.
|
|
453
|
+
&& process.env.OC_SEND_URL && process.env.OC_CHANNEL_ID && process.env.OC_CHANNEL_ADAPTER;
|
|
440
454
|
if (!canAsk) { console.error(gate.message); process.exitCode = 3; return; }
|
|
441
455
|
console.error(`Destructive run without --yes-destructive — asking for chat approval of the exact command...`);
|
|
442
456
|
let decision = "denied";
|
|
@@ -447,7 +461,6 @@ async function run(args) {
|
|
|
447
461
|
tool: t.name, verb: tools.invokedVerb(toolArgs), tier: gate.tier, command: commandLine,
|
|
448
462
|
});
|
|
449
463
|
reqId = res.id;
|
|
450
|
-
const approvals = require("../core/approvals");
|
|
451
464
|
decision = await approvals.waitForDecision(res.id);
|
|
452
465
|
if (decision === "timeout") {
|
|
453
466
|
approvals.detach(res.id);
|
|
@@ -464,8 +477,13 @@ async function run(args) {
|
|
|
464
477
|
`Re-run with --yes-destructive only after the user explicitly approves.`);
|
|
465
478
|
process.exitCode = 3; return;
|
|
466
479
|
}
|
|
480
|
+
const redeemed = approvals.consume(reqId, commandLine);
|
|
481
|
+
if (!redeemed.ok) {
|
|
482
|
+
console.error(`⛔ Approval ${reqId || "(missing)"} rejected: ${redeemed.reason}. The run stays blocked.`);
|
|
483
|
+
process.exitCode = 3; return;
|
|
484
|
+
}
|
|
467
485
|
approvedVia = "chat-approval";
|
|
468
|
-
console.error(`✅
|
|
486
|
+
console.error(`✅ Approval ${reqId} redeemed (single use) — running.`);
|
|
469
487
|
}
|
|
470
488
|
|
|
471
489
|
const missing = tools.missingRequires(t);
|