zora-agent 0.10.6 → 0.11.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/README.md +2 -0
- package/bin/zora-agent +17 -4
- package/dist/channels/capability-resolver.d.ts +34 -0
- package/dist/channels/capability-resolver.d.ts.map +1 -0
- package/dist/channels/capability-resolver.js +53 -0
- package/dist/channels/capability-resolver.js.map +1 -0
- package/dist/channels/channel-adapter.d.ts +42 -0
- package/dist/channels/channel-adapter.d.ts.map +1 -0
- package/dist/channels/channel-adapter.js +8 -0
- package/dist/channels/channel-adapter.js.map +1 -0
- package/dist/channels/channel-audit-log.d.ts +33 -0
- package/dist/channels/channel-audit-log.d.ts.map +1 -0
- package/dist/channels/channel-audit-log.js +67 -0
- package/dist/channels/channel-audit-log.js.map +1 -0
- package/dist/channels/channel-identity-registry.d.ts +103 -0
- package/dist/channels/channel-identity-registry.d.ts.map +1 -0
- package/dist/channels/channel-identity-registry.js +155 -0
- package/dist/channels/channel-identity-registry.js.map +1 -0
- package/dist/channels/channel-manager.d.ts +55 -0
- package/dist/channels/channel-manager.d.ts.map +1 -0
- package/dist/channels/channel-manager.js +149 -0
- package/dist/channels/channel-manager.js.map +1 -0
- package/dist/channels/channel-policy-gate.d.ts +31 -0
- package/dist/channels/channel-policy-gate.d.ts.map +1 -0
- package/dist/channels/channel-policy-gate.js +119 -0
- package/dist/channels/channel-policy-gate.js.map +1 -0
- package/dist/channels/quarantine-processor.d.ts +47 -0
- package/dist/channels/quarantine-processor.d.ts.map +1 -0
- package/dist/channels/quarantine-processor.js +171 -0
- package/dist/channels/quarantine-processor.js.map +1 -0
- package/dist/channels/signal/signal-adapter.d.ts +19 -0
- package/dist/channels/signal/signal-adapter.d.ts.map +1 -0
- package/dist/channels/signal/signal-adapter.js +43 -0
- package/dist/channels/signal/signal-adapter.js.map +1 -0
- package/dist/channels/signal/signal-identity.d.ts +70 -0
- package/dist/channels/signal/signal-identity.d.ts.map +1 -0
- package/dist/channels/signal/signal-identity.js +106 -0
- package/dist/channels/signal/signal-identity.js.map +1 -0
- package/dist/channels/signal/signal-intake-adapter.d.ts +47 -0
- package/dist/channels/signal/signal-intake-adapter.d.ts.map +1 -0
- package/dist/channels/signal/signal-intake-adapter.js +167 -0
- package/dist/channels/signal/signal-intake-adapter.js.map +1 -0
- package/dist/channels/signal/signal-response-gateway.d.ts +33 -0
- package/dist/channels/signal/signal-response-gateway.d.ts.map +1 -0
- package/dist/channels/signal/signal-response-gateway.js +71 -0
- package/dist/channels/signal/signal-response-gateway.js.map +1 -0
- package/dist/channels/telegram/telegram-adapter.d.ts +21 -0
- package/dist/channels/telegram/telegram-adapter.d.ts.map +1 -0
- package/dist/channels/telegram/telegram-adapter.js +76 -0
- package/dist/channels/telegram/telegram-adapter.js.map +1 -0
- package/dist/channels/webhook-server.d.ts +25 -0
- package/dist/channels/webhook-server.d.ts.map +1 -0
- package/dist/channels/webhook-server.js +61 -0
- package/dist/channels/webhook-server.js.map +1 -0
- package/dist/cli/daemon.js +78 -24
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/index.js +6 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/config/defaults.d.ts +2 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +7 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +15 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/dashboard/frontend/dist/assets/index-CPpbKshV.js +277 -0
- package/dist/dashboard/frontend/dist/assets/index-blpbEa6_.css +1 -0
- package/dist/dashboard/frontend/dist/index.html +3 -3
- package/dist/dashboard/server.d.ts +5 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +36 -0
- package/dist/dashboard/server.js.map +1 -1
- package/dist/hooks/built-in/sensitive-file-guard.d.ts +25 -0
- package/dist/hooks/built-in/sensitive-file-guard.d.ts.map +1 -0
- package/dist/hooks/built-in/sensitive-file-guard.js +171 -0
- package/dist/hooks/built-in/sensitive-file-guard.js.map +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/orchestrator/execution-loop.d.ts +2 -0
- package/dist/orchestrator/execution-loop.d.ts.map +1 -1
- package/dist/orchestrator/execution-loop.js +11 -1
- package/dist/orchestrator/execution-loop.js.map +1 -1
- package/dist/orchestrator/orchestrator.d.ts +27 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator.js +172 -3
- package/dist/orchestrator/orchestrator.js.map +1 -1
- package/dist/providers/gemini-provider.d.ts.map +1 -1
- package/dist/providers/gemini-provider.js +15 -1
- package/dist/providers/gemini-provider.js.map +1 -1
- package/dist/types/channel.d.ts +61 -0
- package/dist/types/channel.d.ts.map +1 -0
- package/dist/types/channel.js +23 -0
- package/dist/types/channel.js.map +1 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# Zora
|
|
4
4
|
|
|
5
|
+
[](https://deepwiki.com/ryaker/zora)
|
|
6
|
+
|
|
5
7
|
**Your personal AI agent. Local, secure, and memory-first.**
|
|
6
8
|
|
|
7
9
|
Zora runs on your computer, takes real actions (reads files, runs commands, automates tasks), and actually remembers what it's doing between sessions — without giving up control of your system.
|
package/bin/zora-agent
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
# Strip Claude Code environment variables that cause the claude-agent-sdk to
|
|
3
3
|
# enter CLI mode (CLAUDE_CODE_ENTRYPOINT=cli) or refuse to run (CLAUDECODE=1).
|
|
4
|
-
# These must be unset before Node.js loads any SDK modules
|
|
5
|
-
#
|
|
6
|
-
#
|
|
4
|
+
# These must be unset before Node.js loads any SDK modules.
|
|
5
|
+
#
|
|
6
|
+
# Resolve the real location of this script so the path works whether called
|
|
7
|
+
# directly or through an npm-installed symlink.
|
|
8
|
+
SCRIPT="$0"
|
|
9
|
+
while [ -L "$SCRIPT" ]; do
|
|
10
|
+
DIR="$(cd "$(dirname "$SCRIPT")" && pwd)"
|
|
11
|
+
SCRIPT="$(readlink "$SCRIPT")"
|
|
12
|
+
case "$SCRIPT" in
|
|
13
|
+
/*) ;;
|
|
14
|
+
*) SCRIPT="$DIR/$SCRIPT" ;;
|
|
15
|
+
esac
|
|
16
|
+
done
|
|
17
|
+
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT")" && pwd)"
|
|
18
|
+
ENTRY="$SCRIPT_DIR/../dist/cli/index.js"
|
|
19
|
+
|
|
7
20
|
exec env -u CLAUDECODE \
|
|
8
21
|
-u CLAUDE_CODE_ENTRYPOINT \
|
|
9
22
|
-u CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS \
|
|
10
|
-
node "$
|
|
23
|
+
node "$ENTRY" "$@"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CapabilityResolver — maps (senderPhone, channelId) → CapabilitySet.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order:
|
|
5
|
+
* 1. gate.getRole(senderPhone, channelId)
|
|
6
|
+
* 2. If null → return deniedCapability()
|
|
7
|
+
* 3. registry.getCapabilitySet(role, senderPhone, channelId)
|
|
8
|
+
* 4. If undefined → return deniedCapability()
|
|
9
|
+
*
|
|
10
|
+
* Hot-reload: delegates to registry.reload() + gate rebuild.
|
|
11
|
+
*
|
|
12
|
+
* INVARIANT-1: No tool execution without a valid, current CapabilitySet.
|
|
13
|
+
*/
|
|
14
|
+
import { CapabilitySet } from '../types/channel.js';
|
|
15
|
+
import { ChannelIdentityRegistry } from './channel-identity-registry.js';
|
|
16
|
+
import { ChannelPolicyGate } from './channel-policy-gate.js';
|
|
17
|
+
export declare class CapabilityResolver {
|
|
18
|
+
private readonly _registry;
|
|
19
|
+
private readonly _gate;
|
|
20
|
+
constructor(registry: ChannelIdentityRegistry, gate: ChannelPolicyGate);
|
|
21
|
+
/**
|
|
22
|
+
* Resolves the CapabilitySet for a given (sender, channel) pair.
|
|
23
|
+
* Returns a denied CapabilitySet if the sender is not authorized.
|
|
24
|
+
*
|
|
25
|
+
* INVARIANT-1: Always returns a CapabilitySet — never throws.
|
|
26
|
+
*/
|
|
27
|
+
resolve(senderPhone: string, channelId: string): Promise<CapabilitySet>;
|
|
28
|
+
/**
|
|
29
|
+
* Hot-reload policy from config file.
|
|
30
|
+
* Delegates to registry.reload() — gate rebuilds via onReload callback.
|
|
31
|
+
*/
|
|
32
|
+
reload(): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=capability-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-resolver.d.ts","sourceRoot":"","sources":["../../src/channels/capability-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,aAAa,EAAoB,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAK7D,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;gBAE9B,QAAQ,EAAE,uBAAuB,EAAE,IAAI,EAAE,iBAAiB;IAKtE;;;;;OAKG;IACG,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAkB7E;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAI9B"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CapabilityResolver — maps (senderPhone, channelId) → CapabilitySet.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order:
|
|
5
|
+
* 1. gate.getRole(senderPhone, channelId)
|
|
6
|
+
* 2. If null → return deniedCapability()
|
|
7
|
+
* 3. registry.getCapabilitySet(role, senderPhone, channelId)
|
|
8
|
+
* 4. If undefined → return deniedCapability()
|
|
9
|
+
*
|
|
10
|
+
* Hot-reload: delegates to registry.reload() + gate rebuild.
|
|
11
|
+
*
|
|
12
|
+
* INVARIANT-1: No tool execution without a valid, current CapabilitySet.
|
|
13
|
+
*/
|
|
14
|
+
import { deniedCapability } from '../types/channel.js';
|
|
15
|
+
import { createLogger } from '../utils/logger.js';
|
|
16
|
+
const log = createLogger('capability-resolver');
|
|
17
|
+
export class CapabilityResolver {
|
|
18
|
+
_registry;
|
|
19
|
+
_gate;
|
|
20
|
+
constructor(registry, gate) {
|
|
21
|
+
this._registry = registry;
|
|
22
|
+
this._gate = gate;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolves the CapabilitySet for a given (sender, channel) pair.
|
|
26
|
+
* Returns a denied CapabilitySet if the sender is not authorized.
|
|
27
|
+
*
|
|
28
|
+
* INVARIANT-1: Always returns a CapabilitySet — never throws.
|
|
29
|
+
*/
|
|
30
|
+
async resolve(senderPhone, channelId) {
|
|
31
|
+
const role = await this._gate.getRole(senderPhone, channelId);
|
|
32
|
+
if (!role) {
|
|
33
|
+
log.debug({ senderPhone, channelId }, 'No role found — returning denied capability');
|
|
34
|
+
return { ...deniedCapability(), senderPhone, channelId };
|
|
35
|
+
}
|
|
36
|
+
const cap = this._registry.getCapabilitySet(role, senderPhone, channelId);
|
|
37
|
+
if (!cap) {
|
|
38
|
+
log.warn({ senderPhone, channelId, role }, 'Role found but no capability set configured — denying');
|
|
39
|
+
return { ...deniedCapability(), senderPhone, channelId, role };
|
|
40
|
+
}
|
|
41
|
+
log.debug({ senderPhone, channelId, role, tools: cap.allowedTools.length }, 'Capability resolved');
|
|
42
|
+
return cap;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Hot-reload policy from config file.
|
|
46
|
+
* Delegates to registry.reload() — gate rebuilds via onReload callback.
|
|
47
|
+
*/
|
|
48
|
+
async reload() {
|
|
49
|
+
await this._registry.reload();
|
|
50
|
+
// Gate re-builds itself via its onReload callback registered in init()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=capability-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-resolver.js","sourceRoot":"","sources":["../../src/channels/capability-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAiB,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGtE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,GAAG,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAC;AAEhD,MAAM,OAAO,kBAAkB;IACZ,SAAS,CAA0B;IACnC,KAAK,CAAoB;IAE1C,YAAY,QAAiC,EAAE,IAAuB;QACpE,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,WAAmB,EAAE,SAAiB;QAClD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAE9D,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,GAAG,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,6CAA6C,CAAC,CAAC;YACrF,OAAO,EAAE,GAAG,gBAAgB,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;QAC3D,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;QAC1E,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,uDAAuD,CAAC,CAAC;YACpG,OAAO,EAAE,GAAG,gBAAgB,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACjE,CAAC;QAED,GAAG,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC;QACnG,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM;QACV,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QAC9B,uEAAuE;IACzE,CAAC;CACF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IChannelAdapter — Abstract interface for Zora communication channels.
|
|
3
|
+
*
|
|
4
|
+
* All external integrations (Signal, Telegram, Slack, etc.) implement this
|
|
5
|
+
* to participate in the secure ChannelManager pipeline.
|
|
6
|
+
*/
|
|
7
|
+
import { ChannelIdentity, ChannelMessage } from '../types/channel.js';
|
|
8
|
+
export interface SendOptions {
|
|
9
|
+
/** Original message timestamp for quoting (group replies) */
|
|
10
|
+
quoteTimestamp?: number;
|
|
11
|
+
/** Author identifier for the quoted message (phone or UUID) */
|
|
12
|
+
quoteAuthor?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface IChannelAdapter {
|
|
15
|
+
/**
|
|
16
|
+
* Unique name for this adapter instance (e.g. "signal", "telegram").
|
|
17
|
+
*/
|
|
18
|
+
readonly name: string;
|
|
19
|
+
/**
|
|
20
|
+
* Start the adapter (connect to daemon, start polling, etc.)
|
|
21
|
+
*/
|
|
22
|
+
start(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Gracefully stop the adapter.
|
|
25
|
+
*/
|
|
26
|
+
stop(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Register the message handler called for every inbound message.
|
|
29
|
+
* Only one handler is supported per adapter.
|
|
30
|
+
*/
|
|
31
|
+
onMessage(handler: (msg: ChannelMessage) => Promise<void>): void;
|
|
32
|
+
/**
|
|
33
|
+
* Send a response message back to the channel.
|
|
34
|
+
*
|
|
35
|
+
* @param to - Recipient identity
|
|
36
|
+
* @param channelId - Target channel ("direct" or group ID)
|
|
37
|
+
* @param content - Text response
|
|
38
|
+
* @param options - Optional quoting context
|
|
39
|
+
*/
|
|
40
|
+
send(to: ChannelIdentity, channelId: string, content: string, options?: SendOptions): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=channel-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-adapter.d.ts","sourceRoot":"","sources":["../../src/channels/channel-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,6DAA6D;IAC7D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtB;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAEjE;;;;;;;OAOG;IACH,IAAI,CACF,EAAE,EAAE,eAAe,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IChannelAdapter — Abstract interface for Zora communication channels.
|
|
3
|
+
*
|
|
4
|
+
* All external integrations (Signal, Telegram, Slack, etc.) implement this
|
|
5
|
+
* to participate in the secure ChannelManager pipeline.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=channel-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-adapter.js","sourceRoot":"","sources":["../../src/channels/channel-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChannelAuditLog — Append-only JSONL logger for all channel intake decisions.
|
|
3
|
+
*
|
|
4
|
+
* Logs:
|
|
5
|
+
* - Intake decisions (allow/deny)
|
|
6
|
+
* - Quarantine flags (suspicious patterns)
|
|
7
|
+
* - Tool calls / action budget events
|
|
8
|
+
*
|
|
9
|
+
* Rotation: rotates at 10MB.
|
|
10
|
+
* Location: ~/.zora/audit/channel.log
|
|
11
|
+
*
|
|
12
|
+
* INVARIANT-6: Never log raw message content.
|
|
13
|
+
*/
|
|
14
|
+
export interface AuditEntry {
|
|
15
|
+
timestamp: string;
|
|
16
|
+
adapter: string;
|
|
17
|
+
sender: string;
|
|
18
|
+
channelId: string;
|
|
19
|
+
action: 'intake_allowed' | 'intake_denied' | 'quarantine_flag' | 'tool_call' | 'budget_exhausted';
|
|
20
|
+
status: 'ok' | 'blocked' | 'error';
|
|
21
|
+
metadata?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export declare class ChannelAuditLog {
|
|
24
|
+
private readonly _logPath;
|
|
25
|
+
constructor(baseDir?: string);
|
|
26
|
+
/**
|
|
27
|
+
* Append an entry to the JSONL log.
|
|
28
|
+
*/
|
|
29
|
+
append(entry: Omit<AuditEntry, 'timestamp'>): Promise<void>;
|
|
30
|
+
/** Internal: Rotate log file if it exceeds MAX_LOG_SIZE */
|
|
31
|
+
private _checkRotation;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=channel-audit-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-audit-log.d.ts","sourceRoot":"","sources":["../../src/channels/channel-audit-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,GAAG,eAAe,GAAG,iBAAiB,GAAG,WAAW,GAAG,kBAAkB,CAAC;IAClG,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAID,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,OAAO,CAAC,EAAE,MAAM;IAW5B;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBjE,2DAA2D;YAC7C,cAAc;CAe7B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChannelAuditLog — Append-only JSONL logger for all channel intake decisions.
|
|
3
|
+
*
|
|
4
|
+
* Logs:
|
|
5
|
+
* - Intake decisions (allow/deny)
|
|
6
|
+
* - Quarantine flags (suspicious patterns)
|
|
7
|
+
* - Tool calls / action budget events
|
|
8
|
+
*
|
|
9
|
+
* Rotation: rotates at 10MB.
|
|
10
|
+
* Location: ~/.zora/audit/channel.log
|
|
11
|
+
*
|
|
12
|
+
* INVARIANT-6: Never log raw message content.
|
|
13
|
+
*/
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import os from 'node:os';
|
|
17
|
+
import { createLogger } from '../utils/logger.js';
|
|
18
|
+
const log = createLogger('channel-audit');
|
|
19
|
+
const MAX_LOG_SIZE = 10 * 1024 * 1024; // 10MB
|
|
20
|
+
export class ChannelAuditLog {
|
|
21
|
+
_logPath;
|
|
22
|
+
constructor(baseDir) {
|
|
23
|
+
const root = baseDir ?? path.join(os.homedir(), '.zora');
|
|
24
|
+
this._logPath = path.join(root, 'audit', 'channel.log');
|
|
25
|
+
// Ensure directory exists
|
|
26
|
+
const dir = path.dirname(this._logPath);
|
|
27
|
+
if (!fs.existsSync(dir)) {
|
|
28
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Append an entry to the JSONL log.
|
|
33
|
+
*/
|
|
34
|
+
async append(entry) {
|
|
35
|
+
const fullEntry = {
|
|
36
|
+
timestamp: new Date().toISOString(),
|
|
37
|
+
...entry,
|
|
38
|
+
};
|
|
39
|
+
try {
|
|
40
|
+
// Rotation check before append
|
|
41
|
+
await this._checkRotation();
|
|
42
|
+
const line = JSON.stringify(fullEntry) + '\n';
|
|
43
|
+
await fs.promises.appendFile(this._logPath, line, 'utf-8');
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
log.error({ err, action: entry.action }, 'Failed to write to channel audit log');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Internal: Rotate log file if it exceeds MAX_LOG_SIZE */
|
|
50
|
+
async _checkRotation() {
|
|
51
|
+
try {
|
|
52
|
+
if (!fs.existsSync(this._logPath))
|
|
53
|
+
return;
|
|
54
|
+
const stats = await fs.promises.stat(this._logPath);
|
|
55
|
+
if (stats.size > MAX_LOG_SIZE) {
|
|
56
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
57
|
+
const rotatedPath = `${this._logPath}.${timestamp}.bak`;
|
|
58
|
+
await fs.promises.rename(this._logPath, rotatedPath);
|
|
59
|
+
log.info({ rotatedTo: rotatedPath }, 'Channel audit log rotated');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
log.warn({ err }, 'Log rotation check failed');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=channel-audit-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-audit-log.js","sourceRoot":"","sources":["../../src/channels/channel-audit-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,GAAG,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;AAY1C,MAAM,YAAY,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAE9C,MAAM,OAAO,eAAe;IACT,QAAQ,CAAS;IAElC,YAAY,OAAgB;QAC1B,MAAM,IAAI,GAAG,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;QACzD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QAExD,0BAA0B;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,KAAoC;QAC/C,MAAM,SAAS,GAAe;YAC5B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,GAAG,KAAK;SACT,CAAC;QAEF,IAAI,CAAC;YACH,+BAA+B;YAC/B,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;YAE5B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;YAC9C,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,sCAAsC,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IAED,2DAA2D;IACnD,KAAK,CAAC,cAAc;QAC1B,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,OAAO;YAE1C,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpD,IAAI,KAAK,CAAC,IAAI,GAAG,YAAY,EAAE,CAAC;gBAC9B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;gBACjE,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,QAAQ,IAAI,SAAS,MAAM,CAAC;gBACxD,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;gBACrD,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,2BAA2B,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,2BAA2B,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChannelIdentityRegistry — loads and manages channel policy configuration.
|
|
3
|
+
*
|
|
4
|
+
* Reads config/channel-policy.toml and provides:
|
|
5
|
+
* - User trust definitions (phone → role mappings)
|
|
6
|
+
* - Capability set definitions (role → allowed tools)
|
|
7
|
+
* - Hot-reload on SIGHUP
|
|
8
|
+
*
|
|
9
|
+
* INVARIANT-3: Unknown senders receive NO response
|
|
10
|
+
*/
|
|
11
|
+
import { CapabilitySet } from "../types/channel.js";
|
|
12
|
+
export interface UserPolicy {
|
|
13
|
+
phone: string;
|
|
14
|
+
name: string;
|
|
15
|
+
channels: string[];
|
|
16
|
+
role: string;
|
|
17
|
+
dm_role?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface CapabilitySetConfig {
|
|
20
|
+
tools: string[];
|
|
21
|
+
destructive_ops: boolean;
|
|
22
|
+
action_budget: number;
|
|
23
|
+
param_constraints?: {
|
|
24
|
+
bash?: {
|
|
25
|
+
command_allowlist?: string[];
|
|
26
|
+
command_blocklist?: string[];
|
|
27
|
+
};
|
|
28
|
+
write_file?: {
|
|
29
|
+
path_allowlist?: string[];
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface ChannelPolicyConfig {
|
|
34
|
+
signal?: {
|
|
35
|
+
phone_number?: string;
|
|
36
|
+
signal_cli_path?: string;
|
|
37
|
+
linked_device?: boolean;
|
|
38
|
+
daemon_port?: number;
|
|
39
|
+
auto_trust_new_identities?: boolean;
|
|
40
|
+
};
|
|
41
|
+
channels?: {
|
|
42
|
+
prompt_injection?: {
|
|
43
|
+
enabled?: boolean;
|
|
44
|
+
scanner_url?: string;
|
|
45
|
+
block_on_high_risk?: boolean;
|
|
46
|
+
};
|
|
47
|
+
quarantine?: {
|
|
48
|
+
enabled?: boolean;
|
|
49
|
+
model?: string;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
channel_policy?: {
|
|
53
|
+
users?: UserPolicy[];
|
|
54
|
+
};
|
|
55
|
+
capability_sets?: Record<string, CapabilitySetConfig>;
|
|
56
|
+
}
|
|
57
|
+
export declare class ChannelIdentityRegistry {
|
|
58
|
+
private config;
|
|
59
|
+
private configPath;
|
|
60
|
+
private reloadCallbacks;
|
|
61
|
+
private constructor();
|
|
62
|
+
/**
|
|
63
|
+
* Load registry from TOML file.
|
|
64
|
+
* Throws if file doesn't exist or has invalid structure.
|
|
65
|
+
*/
|
|
66
|
+
static load(configPath: string): Promise<ChannelIdentityRegistry>;
|
|
67
|
+
/**
|
|
68
|
+
* Reload configuration from disk.
|
|
69
|
+
* Called on startup and on SIGHUP.
|
|
70
|
+
*/
|
|
71
|
+
reload(): Promise<void>;
|
|
72
|
+
/** Register a callback to be called when config is hot-reloaded */
|
|
73
|
+
onReload(callback: () => void): void;
|
|
74
|
+
/** Get all user policies */
|
|
75
|
+
getUsers(): UserPolicy[];
|
|
76
|
+
/**
|
|
77
|
+
* Get a specific user policy by phone number.
|
|
78
|
+
* Normalizes input to handle minor formatting differences before lookup.
|
|
79
|
+
*/
|
|
80
|
+
getUser(phone: string): UserPolicy | undefined;
|
|
81
|
+
/** Get all capability set definitions */
|
|
82
|
+
getCapabilitySets(): Record<string, CapabilitySetConfig>;
|
|
83
|
+
/** Get raw capability set config (snake_case) for a given role */
|
|
84
|
+
getCapabilitySetConfig(role: string): CapabilitySetConfig | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* Get a fully-typed CapabilitySet (camelCase) for a given role and sender.
|
|
87
|
+
* Transforms snake_case TOML config fields to the CapabilitySet interface.
|
|
88
|
+
* Returns undefined when the role is not found.
|
|
89
|
+
*/
|
|
90
|
+
getCapabilitySet(role: string, senderPhone: string, channelId: string): CapabilitySet | undefined;
|
|
91
|
+
/** Get Signal daemon configuration */
|
|
92
|
+
getSignalConfig(): ChannelPolicyConfig["signal"];
|
|
93
|
+
/** Get quarantine model name */
|
|
94
|
+
getQuarantineModel(): string;
|
|
95
|
+
/** Get prompt injection config */
|
|
96
|
+
getPromptInjectionConfig(): NonNullable<ChannelPolicyConfig["channels"]>["prompt_injection"];
|
|
97
|
+
/**
|
|
98
|
+
* Set up hot-reload via SIGHUP.
|
|
99
|
+
* Call once after registry is loaded.
|
|
100
|
+
*/
|
|
101
|
+
listenForReload(): void;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=channel-identity-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-identity-registry.d.ts","sourceRoot":"","sources":["../../src/channels/channel-identity-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAiCpD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE;QAClB,IAAI,CAAC,EAAE;YAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QACtE,UAAU,CAAC,EAAE;YAAE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;KAC5C,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE;QACP,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,yBAAyB,CAAC,EAAE,OAAO,CAAC;KACrC,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,gBAAgB,CAAC,EAAE;YACjB,OAAO,CAAC,EAAE,OAAO,CAAC;YAClB,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;SAC9B,CAAC;QACF,UAAU,CAAC,EAAE;YACX,OAAO,CAAC,EAAE,OAAO,CAAC;YAClB,KAAK,CAAC,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;IACF,cAAc,CAAC,EAAE;QACf,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;KACtB,CAAC;IACF,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CACvD;AAED,qBAAa,uBAAuB;IAClC,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAyB;IAEhD,OAAO;IAIP;;;OAGG;WACU,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAMvE;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAO7B,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAIpC,4BAA4B;IAC5B,QAAQ,IAAI,UAAU,EAAE;IAIxB;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAU9C,yCAAyC;IACzC,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC;IAIxD,kEAAkE;IAClE,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAIrE;;;;OAIG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAsBjG,sCAAsC;IACtC,eAAe,IAAI,mBAAmB,CAAC,QAAQ,CAAC;IAIhD,gCAAgC;IAChC,kBAAkB,IAAI,MAAM;IAI5B,kCAAkC;IAClC,wBAAwB,IAAI,WAAW,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,CAAC;IAI5F;;;OAGG;IACH,eAAe,IAAI,IAAI;CAaxB"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChannelIdentityRegistry — loads and manages channel policy configuration.
|
|
3
|
+
*
|
|
4
|
+
* Reads config/channel-policy.toml and provides:
|
|
5
|
+
* - User trust definitions (phone → role mappings)
|
|
6
|
+
* - Capability set definitions (role → allowed tools)
|
|
7
|
+
* - Hot-reload on SIGHUP
|
|
8
|
+
*
|
|
9
|
+
* INVARIANT-3: Unknown senders receive NO response
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync } from "fs";
|
|
12
|
+
import { resolve } from "path";
|
|
13
|
+
// Using smol-toml or @iarna/toml if available, fallback to manual parse
|
|
14
|
+
// Try dynamic import to handle either package
|
|
15
|
+
async function loadTomlParser() {
|
|
16
|
+
try {
|
|
17
|
+
const { parse } = await import("smol-toml");
|
|
18
|
+
return parse;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
try {
|
|
22
|
+
const toml = await import("@iarna/toml");
|
|
23
|
+
return toml.parse;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Fallback: basic TOML-like parser for our specific config shape
|
|
27
|
+
// This handles the exact structure of channel-policy.toml
|
|
28
|
+
return parseChannelPolicyToml;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Basic TOML parser for channel-policy.toml structure.
|
|
34
|
+
* Only handles the exact keys we need — not a general TOML parser.
|
|
35
|
+
*/
|
|
36
|
+
function parseChannelPolicyToml(_content) {
|
|
37
|
+
// We'll use a JSON-compatible approach: convert TOML to a JS object manually
|
|
38
|
+
// This is a limited fallback — recommend installing smol-toml
|
|
39
|
+
throw new Error("No TOML parser available. Install smol-toml: npm install smol-toml\n" +
|
|
40
|
+
"Or @iarna/toml: npm install @iarna/toml");
|
|
41
|
+
}
|
|
42
|
+
export class ChannelIdentityRegistry {
|
|
43
|
+
config = {};
|
|
44
|
+
configPath;
|
|
45
|
+
reloadCallbacks = [];
|
|
46
|
+
constructor(configPath) {
|
|
47
|
+
this.configPath = resolve(configPath);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Load registry from TOML file.
|
|
51
|
+
* Throws if file doesn't exist or has invalid structure.
|
|
52
|
+
*/
|
|
53
|
+
static async load(configPath) {
|
|
54
|
+
const registry = new ChannelIdentityRegistry(configPath);
|
|
55
|
+
await registry.reload();
|
|
56
|
+
return registry;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Reload configuration from disk.
|
|
60
|
+
* Called on startup and on SIGHUP.
|
|
61
|
+
*/
|
|
62
|
+
async reload() {
|
|
63
|
+
const parse = await loadTomlParser();
|
|
64
|
+
const content = readFileSync(this.configPath, "utf-8");
|
|
65
|
+
this.config = parse(content);
|
|
66
|
+
this.reloadCallbacks.forEach(cb => cb());
|
|
67
|
+
}
|
|
68
|
+
/** Register a callback to be called when config is hot-reloaded */
|
|
69
|
+
onReload(callback) {
|
|
70
|
+
this.reloadCallbacks.push(callback);
|
|
71
|
+
}
|
|
72
|
+
/** Get all user policies */
|
|
73
|
+
getUsers() {
|
|
74
|
+
return this.config.channel_policy?.users ?? [];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get a specific user policy by phone number.
|
|
78
|
+
* Normalizes input to handle minor formatting differences before lookup.
|
|
79
|
+
*/
|
|
80
|
+
getUser(phone) {
|
|
81
|
+
// Normalize both sides: strip formatting, ensure + prefix for consistent comparison.
|
|
82
|
+
const norm = (p) => {
|
|
83
|
+
const stripped = p.trim().replace(/[\s\-().]/g, "");
|
|
84
|
+
return stripped.startsWith("+") ? stripped : "+" + stripped;
|
|
85
|
+
};
|
|
86
|
+
const lookup = norm(phone);
|
|
87
|
+
return this.getUsers().find(u => norm(u.phone) === lookup);
|
|
88
|
+
}
|
|
89
|
+
/** Get all capability set definitions */
|
|
90
|
+
getCapabilitySets() {
|
|
91
|
+
return this.config.capability_sets ?? {};
|
|
92
|
+
}
|
|
93
|
+
/** Get raw capability set config (snake_case) for a given role */
|
|
94
|
+
getCapabilitySetConfig(role) {
|
|
95
|
+
return this.getCapabilitySets()[role];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get a fully-typed CapabilitySet (camelCase) for a given role and sender.
|
|
99
|
+
* Transforms snake_case TOML config fields to the CapabilitySet interface.
|
|
100
|
+
* Returns undefined when the role is not found.
|
|
101
|
+
*/
|
|
102
|
+
getCapabilitySet(role, senderPhone, channelId) {
|
|
103
|
+
const cfg = this.getCapabilitySetConfig(role);
|
|
104
|
+
if (!cfg)
|
|
105
|
+
return undefined;
|
|
106
|
+
return {
|
|
107
|
+
senderPhone,
|
|
108
|
+
channelId,
|
|
109
|
+
role,
|
|
110
|
+
allowedTools: cfg.tools ?? [],
|
|
111
|
+
destructiveOpsAllowed: cfg.destructive_ops ?? false,
|
|
112
|
+
actionBudget: cfg.action_budget ?? 0,
|
|
113
|
+
paramConstraints: cfg.param_constraints ? {
|
|
114
|
+
bash: cfg.param_constraints.bash ? {
|
|
115
|
+
commandAllowlist: cfg.param_constraints.bash.command_allowlist,
|
|
116
|
+
commandBlocklist: cfg.param_constraints.bash.command_blocklist,
|
|
117
|
+
} : undefined,
|
|
118
|
+
write_file: cfg.param_constraints.write_file ? {
|
|
119
|
+
pathAllowlist: cfg.param_constraints.write_file.path_allowlist,
|
|
120
|
+
} : undefined,
|
|
121
|
+
} : undefined,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/** Get Signal daemon configuration */
|
|
125
|
+
getSignalConfig() {
|
|
126
|
+
return this.config.signal;
|
|
127
|
+
}
|
|
128
|
+
/** Get quarantine model name */
|
|
129
|
+
getQuarantineModel() {
|
|
130
|
+
return this.config.channels?.quarantine?.model ?? "claude-haiku-4-5-20251001";
|
|
131
|
+
}
|
|
132
|
+
/** Get prompt injection config */
|
|
133
|
+
getPromptInjectionConfig() {
|
|
134
|
+
return this.config.channels?.prompt_injection;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Set up hot-reload via SIGHUP.
|
|
138
|
+
* Call once after registry is loaded.
|
|
139
|
+
*/
|
|
140
|
+
listenForReload() {
|
|
141
|
+
process.on("SIGHUP", async () => {
|
|
142
|
+
try {
|
|
143
|
+
console.log("[policy] SIGHUP received — reloading channel policy...");
|
|
144
|
+
await this.reload();
|
|
145
|
+
const userCount = this.getUsers().length;
|
|
146
|
+
const setCount = Object.keys(this.getCapabilitySets()).length;
|
|
147
|
+
console.log(`[policy] Config reloaded: ${userCount} users, ${setCount} capability sets`);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
console.error("[policy] Config reload failed:", err);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=channel-identity-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-identity-registry.js","sourceRoot":"","sources":["../../src/channels/channel-identity-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChannelManager — Orchestrates the secure message pipeline for all channels.
|
|
3
|
+
*
|
|
4
|
+
* Single pipeline:
|
|
5
|
+
* policy gate → capability resolver → quarantine → orchestrator → response.
|
|
6
|
+
*
|
|
7
|
+
* Every channel (Signal, Telegram, etc.) uses this path. No bypass.
|
|
8
|
+
*
|
|
9
|
+
* INVARIANT-9: All channels use ChannelManager.handleMessage() — no bypass path.
|
|
10
|
+
*/
|
|
11
|
+
import { ChannelMessage } from '../types/channel.js';
|
|
12
|
+
import { IChannelAdapter } from './channel-adapter.js';
|
|
13
|
+
import { ChannelPolicyGate } from './channel-policy-gate.js';
|
|
14
|
+
import { CapabilityResolver } from './capability-resolver.js';
|
|
15
|
+
import { QuarantineProcessor } from './quarantine-processor.js';
|
|
16
|
+
import { ChannelAuditLog } from './channel-audit-log.js';
|
|
17
|
+
/** Interface for the Orchestrator's submitTask method used by ChannelManager */
|
|
18
|
+
interface TaskSubmittable {
|
|
19
|
+
submitTask(options: {
|
|
20
|
+
prompt: string;
|
|
21
|
+
channelContext: {
|
|
22
|
+
capability: any;
|
|
23
|
+
channelMessage: ChannelMessage;
|
|
24
|
+
};
|
|
25
|
+
onEvent: (event: any) => void;
|
|
26
|
+
}): Promise<string>;
|
|
27
|
+
}
|
|
28
|
+
export declare class ChannelManager {
|
|
29
|
+
private readonly _adapters;
|
|
30
|
+
private readonly _orchestrator;
|
|
31
|
+
private readonly _gate;
|
|
32
|
+
private readonly _resolver;
|
|
33
|
+
private readonly _quarantine;
|
|
34
|
+
private readonly _audit;
|
|
35
|
+
constructor(orchestrator: TaskSubmittable, gate: ChannelPolicyGate, resolver: CapabilityResolver, quarantine: QuarantineProcessor, audit?: ChannelAuditLog | null);
|
|
36
|
+
/**
|
|
37
|
+
* Register a communication adapter and start its message listener.
|
|
38
|
+
*/
|
|
39
|
+
registerAdapter(adapter: IChannelAdapter): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Start all registered adapters.
|
|
42
|
+
*/
|
|
43
|
+
start(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Stop all registered adapters.
|
|
46
|
+
*/
|
|
47
|
+
stop(): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* The secure message pipeline.
|
|
50
|
+
* INVARIANT-9: All channels use this path.
|
|
51
|
+
*/
|
|
52
|
+
handleMessage(adapter: IChannelAdapter, msg: ChannelMessage): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=channel-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-manager.d.ts","sourceRoot":"","sources":["../../src/channels/channel-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAKzD,gFAAgF;AAChF,UAAU,eAAe;IACvB,UAAU,CAAC,OAAO,EAAE;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,EAAE;YACd,UAAU,EAAE,GAAG,CAAC;YAChB,cAAc,EAAE,cAAc,CAAC;SAChC,CAAC;QACF,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;KAC/B,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAChE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;gBAG9C,YAAY,EAAE,eAAe,EAC7B,IAAI,EAAE,iBAAiB,EACvB,QAAQ,EAAE,kBAAkB,EAC5B,UAAU,EAAE,mBAAmB,EAC/B,KAAK,GAAE,eAAe,GAAG,IAAW;IAStC;;OAEG;IACG,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9D;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CA4FlF"}
|