@aitne/daemon 0.1.9 → 0.1.10
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/dist/api/env-writer.d.ts +1 -0
- package/dist/api/env-writer.js +9 -2
- package/dist/api/routes/agent-schedule.js +5 -1
- package/dist/api/routes/apple-calendar.js +4 -1
- package/dist/api/routes/calendar.js +12 -2
- package/dist/api/routes/context/path-resolve.js +6 -1
- package/dist/api/routes/context/permissions.js +9 -0
- package/dist/api/routes/dashboard/config.js +10 -0
- package/dist/api/routes/dashboard/oauth-google.js +5 -3
- package/dist/api/routes/feedback.d.ts +3 -0
- package/dist/api/routes/feedback.js +349 -0
- package/dist/api/routes/git.js +10 -3
- package/dist/api/routes/github.js +5 -1
- package/dist/api/routes/mcp.js +65 -13
- package/dist/api/server.js +3 -0
- package/dist/bootstrap/event-pipeline.js +1 -1
- package/dist/config.js +6 -0
- package/dist/core/backends/gemini-cli-core.js +13 -0
- package/dist/core/backends/plan-presets.js +8 -3
- package/dist/core/context-builder.js +149 -3
- package/dist/core/context-paths.d.ts +10 -0
- package/dist/core/context-paths.js +16 -0
- package/dist/core/daemon-api-cli.js +1 -1
- package/dist/core/dispatcher-message-handler.js +7 -0
- package/dist/core/dispatcher-scheduled-tasks.d.ts +41 -0
- package/dist/core/dispatcher-scheduled-tasks.js +267 -2
- package/dist/core/dispatcher.js +13 -1
- package/dist/core/feedback/consolidation-prep.d.ts +94 -0
- package/dist/core/feedback/consolidation-prep.js +242 -0
- package/dist/core/feedback/eviction-scorer.d.ts +81 -0
- package/dist/core/feedback/eviction-scorer.js +132 -0
- package/dist/core/feedback/lesson-format.d.ts +79 -0
- package/dist/core/feedback/lesson-format.js +194 -0
- package/dist/core/feedback/lesson-injection.d.ts +98 -0
- package/dist/core/feedback/lesson-injection.js +159 -0
- package/dist/core/feedback/lesson-merge.d.ts +51 -0
- package/dist/core/feedback/lesson-merge.js +88 -0
- package/dist/core/feedback/lesson-store-overview.d.ts +42 -0
- package/dist/core/feedback/lesson-store-overview.js +38 -0
- package/dist/core/feedback/promotion-gate.d.ts +69 -0
- package/dist/core/feedback/promotion-gate.js +117 -0
- package/dist/core/feedback/regeneralization-prep.d.ts +87 -0
- package/dist/core/feedback/regeneralization-prep.js +139 -0
- package/dist/core/feedback/scope-parser.d.ts +86 -0
- package/dist/core/feedback/scope-parser.js +141 -0
- package/dist/core/injection-policy.d.ts +82 -0
- package/dist/core/injection-policy.js +58 -0
- package/dist/core/signal-detector.d.ts +39 -1
- package/dist/core/signal-detector.js +277 -24
- package/dist/core/today-direct-writer.d.ts +59 -13
- package/dist/core/today-direct-writer.js +90 -13
- package/dist/core/wiki/wiki-fts.js +13 -6
- package/dist/db/feedback-signals-store.d.ts +77 -0
- package/dist/db/feedback-signals-store.js +144 -0
- package/dist/db/migrations.js +50 -0
- package/dist/db/schema.js +43 -6
- package/dist/safety/always-disallowed.d.ts +1 -1
- package/dist/safety/always-disallowed.js +39 -0
- package/dist/safety/risk-classifier.js +22 -7
- package/dist/services/browser-history/automation/egress-denylist.js +18 -2
- package/dist/services/browser-history/lifecycle/platform.js +44 -2
- package/dist/services/mcp/probe.js +30 -8
- package/dist/settings/runtime-settings.d.ts +8 -2
- package/dist/settings/runtime-settings.js +12 -0
- package/package.json +2 -2
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Daemon-direct
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Daemon-direct, lock-aware writes to today.md that run *before* (or
|
|
3
|
+
* instead of) an agent session — bypassing `/api/context/*` because there
|
|
4
|
+
* is no subprocess to issue the curl call from. Two operations live here:
|
|
5
|
+
*
|
|
6
|
+
* 1. {@link appendAgentLogLine} — append a single `## Agent Log` bullet.
|
|
7
|
+
* Used by the three-stage hourly_check gate (cost-reduction-structural
|
|
8
|
+
* §B) on the stage0_silent / stage2_log_only paths so a "no-op" cron
|
|
9
|
+
* tick still leaves an audit trail without paying for an LLM session.
|
|
10
|
+
* 2. {@link ensureTodaySkeleton} — seed the canonical empty skeleton when
|
|
11
|
+
* today.md is **absent**, so a section-only refresh routine has a valid
|
|
12
|
+
* PATCH target instead of 404-ing and budget-burning on full-file PUTs.
|
|
7
13
|
*
|
|
8
14
|
* Why this lives in the daemon (not /api/context/* via curl):
|
|
9
15
|
* - These paths run *before* the agent is spawned. There is no
|
|
@@ -12,15 +18,16 @@
|
|
|
12
18
|
* `context-staleness.ts`) — the same tier the PATCH route already
|
|
13
19
|
* classifies for `## Agent Log` appends. Bypassing the route is
|
|
14
20
|
* fine because we never touch the prompt-context-changed hook here.
|
|
15
|
-
* - The today-write-lock invariant is preserved:
|
|
16
|
-
* mutating the file, so morning_routine and direct writes
|
|
17
|
-
* interleave.
|
|
21
|
+
* - The today-write-lock invariant is preserved: both functions acquire
|
|
22
|
+
* it before mutating the file, so morning_routine and direct writes
|
|
23
|
+
* never interleave.
|
|
18
24
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
25
|
+
* Synthesis boundary: `appendAgentLogLine` NEVER synthesizes structure —
|
|
26
|
+
* a missing / malformed / heading-less file returns false and the gate
|
|
27
|
+
* caller proceeds. `ensureTodaySkeleton` synthesizes ONLY the empty
|
|
28
|
+
* skeleton, ONLY when the file is entirely absent, and never touches a
|
|
29
|
+
* present file. Neither populates today.md — full creation and repair stay
|
|
30
|
+
* the morning routine's job.
|
|
24
31
|
*/
|
|
25
32
|
import type { TodayWriteLockManager } from "./today-write-lock.js";
|
|
26
33
|
export interface AppendAgentLogLineInput {
|
|
@@ -61,6 +68,45 @@ export interface AppendAgentLogLineResult {
|
|
|
61
68
|
* writers cleanly.
|
|
62
69
|
*/
|
|
63
70
|
export declare function appendAgentLogLine(input: AppendAgentLogLineInput): Promise<AppendAgentLogLineResult>;
|
|
71
|
+
export interface EnsureTodaySkeletonInput {
|
|
72
|
+
contextDir: string;
|
|
73
|
+
todayWriteLock: TodayWriteLockManager;
|
|
74
|
+
}
|
|
75
|
+
export interface EnsureTodaySkeletonResult {
|
|
76
|
+
seeded: boolean;
|
|
77
|
+
reason?: "already_present" | "lock_unavailable" | "io_error";
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Guarantee a `today.md` working surface exists before a section-only
|
|
81
|
+
* refresh routine (`routine.today_refresh`) assumes it.
|
|
82
|
+
*
|
|
83
|
+
* `rotateDayFiles()` intentionally renames `today.md` → `yesterday.md` at
|
|
84
|
+
* the day boundary and relies on the morning routine to recreate the
|
|
85
|
+
* dated file. When the morning routine has not run yet — or failed (e.g.
|
|
86
|
+
* a quota/budget death with no fallback backend) — `today.md` is absent
|
|
87
|
+
* and the refresh task flow's `PATCH section=user_schedule` 404s. The
|
|
88
|
+
* agent then improvises full-file `PUT`s, which the strict
|
|
89
|
+
* `validateTodayContent` schema rejects line-by-line; on a single-backend
|
|
90
|
+
* binding with a tight per-turn budget that loop tips into
|
|
91
|
+
* `BackendQuotaError(max_budget_usd)` and the refresh dies without ever
|
|
92
|
+
* writing the file — the "Refresh Today does nothing" symptom.
|
|
93
|
+
*
|
|
94
|
+
* This deterministic pre-step removes that whole failure mode: when the
|
|
95
|
+
* file is **entirely absent** we seed the canonical empty skeleton so the
|
|
96
|
+
* agent's section PATCH always has a valid target. A file that already
|
|
97
|
+
* exists is left byte-untouched — a valid dated file OR the legacy
|
|
98
|
+
* `# Today` bridge stub both accept the section PATCH (the route's
|
|
99
|
+
* `allowLegacyToday` branch). We never repair a malformed-but-present
|
|
100
|
+
* file and never overwrite user content; full creation/repair stays the
|
|
101
|
+
* morning routine's job. The seeded skeleton is dateless (`# Today`), so
|
|
102
|
+
* it does NOT satisfy `hasCurrentAgentDayTodayMd()` and the pending
|
|
103
|
+
* morning-routine retry still fires and upgrades it.
|
|
104
|
+
*
|
|
105
|
+
* Lock-aware exactly like {@link appendAgentLogLine}: if the morning
|
|
106
|
+
* routine holds the today-write-lock (mid-creation) we skip and let it
|
|
107
|
+
* win — the refresh session then 409-defers on its own PATCH.
|
|
108
|
+
*/
|
|
109
|
+
export declare function ensureTodaySkeleton(input: EnsureTodaySkeletonInput): Promise<EnsureTodaySkeletonResult>;
|
|
64
110
|
/**
|
|
65
111
|
* Splice a new bullet line into the `## Agent Log` section, immediately
|
|
66
112
|
* before the next `## ` heading or end-of-file. Returns null when the
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Daemon-direct
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Daemon-direct, lock-aware writes to today.md that run *before* (or
|
|
3
|
+
* instead of) an agent session — bypassing `/api/context/*` because there
|
|
4
|
+
* is no subprocess to issue the curl call from. Two operations live here:
|
|
5
|
+
*
|
|
6
|
+
* 1. {@link appendAgentLogLine} — append a single `## Agent Log` bullet.
|
|
7
|
+
* Used by the three-stage hourly_check gate (cost-reduction-structural
|
|
8
|
+
* §B) on the stage0_silent / stage2_log_only paths so a "no-op" cron
|
|
9
|
+
* tick still leaves an audit trail without paying for an LLM session.
|
|
10
|
+
* 2. {@link ensureTodaySkeleton} — seed the canonical empty skeleton when
|
|
11
|
+
* today.md is **absent**, so a section-only refresh routine has a valid
|
|
12
|
+
* PATCH target instead of 404-ing and budget-burning on full-file PUTs.
|
|
7
13
|
*
|
|
8
14
|
* Why this lives in the daemon (not /api/context/* via curl):
|
|
9
15
|
* - These paths run *before* the agent is spawned. There is no
|
|
@@ -12,20 +18,22 @@
|
|
|
12
18
|
* `context-staleness.ts`) — the same tier the PATCH route already
|
|
13
19
|
* classifies for `## Agent Log` appends. Bypassing the route is
|
|
14
20
|
* fine because we never touch the prompt-context-changed hook here.
|
|
15
|
-
* - The today-write-lock invariant is preserved:
|
|
16
|
-
* mutating the file, so morning_routine and direct writes
|
|
17
|
-
* interleave.
|
|
21
|
+
* - The today-write-lock invariant is preserved: both functions acquire
|
|
22
|
+
* it before mutating the file, so morning_routine and direct writes
|
|
23
|
+
* never interleave.
|
|
18
24
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
25
|
+
* Synthesis boundary: `appendAgentLogLine` NEVER synthesizes structure —
|
|
26
|
+
* a missing / malformed / heading-less file returns false and the gate
|
|
27
|
+
* caller proceeds. `ensureTodaySkeleton` synthesizes ONLY the empty
|
|
28
|
+
* skeleton, ONLY when the file is entirely absent, and never touches a
|
|
29
|
+
* present file. Neither populates today.md — full creation and repair stay
|
|
30
|
+
* the morning routine's job.
|
|
24
31
|
*/
|
|
25
32
|
import { existsSync, readFileSync } from "node:fs";
|
|
26
33
|
import { writeFileAtomically } from "./atomic-write.js";
|
|
27
34
|
import { serializeContextFileWrite } from "./context-file-serializer.js";
|
|
28
35
|
import { fullPath, CONTEXT_RELATIVE_PATHS } from "./context-paths.js";
|
|
36
|
+
import { FALLBACK_PLACEHOLDERS } from "./skeleton.js";
|
|
29
37
|
import { createLogger } from "../logging.js";
|
|
30
38
|
const logger = createLogger("today-direct-writer");
|
|
31
39
|
const AGENT_LOG_HEADER = "## Agent Log";
|
|
@@ -87,6 +95,75 @@ export async function appendAgentLogLine(input) {
|
|
|
87
95
|
input.todayWriteLock.release(lock.lockId);
|
|
88
96
|
}
|
|
89
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Canonical empty `today.md` skeleton, reused byte-for-byte from the
|
|
100
|
+
* boot-time seeder (`skeleton.ts`) so a refresh-path seed and a
|
|
101
|
+
* fresh-install seed produce identical structure. `skeleton.test.ts`
|
|
102
|
+
* asserts this placeholder matches `agent-assets/templates/state/today.md`
|
|
103
|
+
* byte-for-byte, so the two definitions never drift. The non-null
|
|
104
|
+
* assertion is safe: the key is a literal entry of `FALLBACK_PLACEHOLDERS`.
|
|
105
|
+
*/
|
|
106
|
+
const TODAY_SKELETON = FALLBACK_PLACEHOLDERS[CONTEXT_RELATIVE_PATHS.today];
|
|
107
|
+
/**
|
|
108
|
+
* Guarantee a `today.md` working surface exists before a section-only
|
|
109
|
+
* refresh routine (`routine.today_refresh`) assumes it.
|
|
110
|
+
*
|
|
111
|
+
* `rotateDayFiles()` intentionally renames `today.md` → `yesterday.md` at
|
|
112
|
+
* the day boundary and relies on the morning routine to recreate the
|
|
113
|
+
* dated file. When the morning routine has not run yet — or failed (e.g.
|
|
114
|
+
* a quota/budget death with no fallback backend) — `today.md` is absent
|
|
115
|
+
* and the refresh task flow's `PATCH section=user_schedule` 404s. The
|
|
116
|
+
* agent then improvises full-file `PUT`s, which the strict
|
|
117
|
+
* `validateTodayContent` schema rejects line-by-line; on a single-backend
|
|
118
|
+
* binding with a tight per-turn budget that loop tips into
|
|
119
|
+
* `BackendQuotaError(max_budget_usd)` and the refresh dies without ever
|
|
120
|
+
* writing the file — the "Refresh Today does nothing" symptom.
|
|
121
|
+
*
|
|
122
|
+
* This deterministic pre-step removes that whole failure mode: when the
|
|
123
|
+
* file is **entirely absent** we seed the canonical empty skeleton so the
|
|
124
|
+
* agent's section PATCH always has a valid target. A file that already
|
|
125
|
+
* exists is left byte-untouched — a valid dated file OR the legacy
|
|
126
|
+
* `# Today` bridge stub both accept the section PATCH (the route's
|
|
127
|
+
* `allowLegacyToday` branch). We never repair a malformed-but-present
|
|
128
|
+
* file and never overwrite user content; full creation/repair stays the
|
|
129
|
+
* morning routine's job. The seeded skeleton is dateless (`# Today`), so
|
|
130
|
+
* it does NOT satisfy `hasCurrentAgentDayTodayMd()` and the pending
|
|
131
|
+
* morning-routine retry still fires and upgrades it.
|
|
132
|
+
*
|
|
133
|
+
* Lock-aware exactly like {@link appendAgentLogLine}: if the morning
|
|
134
|
+
* routine holds the today-write-lock (mid-creation) we skip and let it
|
|
135
|
+
* win — the refresh session then 409-defers on its own PATCH.
|
|
136
|
+
*/
|
|
137
|
+
export async function ensureTodaySkeleton(input) {
|
|
138
|
+
const lock = input.todayWriteLock.acquire();
|
|
139
|
+
if (!lock.ok) {
|
|
140
|
+
logger.info({ holder: lock.holder }, "Skipping today.md skeleton seed — today-write-lock held");
|
|
141
|
+
return { seeded: false, reason: "lock_unavailable" };
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const path = fullPath(input.contextDir, CONTEXT_RELATIVE_PATHS.today);
|
|
145
|
+
return await serializeContextFileWrite(path, () => {
|
|
146
|
+
if (existsSync(path)) {
|
|
147
|
+
return {
|
|
148
|
+
seeded: false,
|
|
149
|
+
reason: "already_present",
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
writeFileAtomically(path, TODAY_SKELETON);
|
|
154
|
+
logger.info({ path }, "Seeded today.md skeleton for refresh — file was absent (morning routine not yet run for the agent-day)");
|
|
155
|
+
return { seeded: true };
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
logger.error({ err, path }, "Failed to seed today.md skeleton");
|
|
159
|
+
return { seeded: false, reason: "io_error" };
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
input.todayWriteLock.release(lock.lockId);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
90
167
|
/**
|
|
91
168
|
* Splice a new bullet line into the `## Agent Log` section, immediately
|
|
92
169
|
* before the next `## ` heading or end-of-file. Returns null when the
|
|
@@ -110,15 +110,22 @@ function extractTitleAndBody(content) {
|
|
|
110
110
|
return { title, body };
|
|
111
111
|
}
|
|
112
112
|
function stripFrontmatter(content) {
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
// Obsidian vaults authored/synced on Windows (or checked out under
|
|
114
|
+
// git core.autocrlf=true) are CRLF. The frontmatter fence gate below is
|
|
115
|
+
// LF-only, so without this normalize a `---\r\n…---\r\n` block leaks into
|
|
116
|
+
// the indexed body and the title fallback never populates. Indexed
|
|
117
|
+
// body/title are search tokens only and never round-trip to disk, so
|
|
118
|
+
// collapsing interior CRLF to LF is harmless (LF input is unchanged).
|
|
119
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
120
|
+
if (!normalized.startsWith("---\n")) {
|
|
121
|
+
return { frontmatterKeys: {}, body: normalized };
|
|
115
122
|
}
|
|
116
|
-
const end =
|
|
123
|
+
const end = normalized.indexOf("\n---\n", 4);
|
|
117
124
|
if (end < 0) {
|
|
118
|
-
return { frontmatterKeys: {}, body:
|
|
125
|
+
return { frontmatterKeys: {}, body: normalized };
|
|
119
126
|
}
|
|
120
|
-
const frontmatter =
|
|
121
|
-
const body =
|
|
127
|
+
const frontmatter = normalized.slice(4, end);
|
|
128
|
+
const body = normalized.slice(end + 5);
|
|
122
129
|
const keys = {};
|
|
123
130
|
for (const line of frontmatter.split("\n")) {
|
|
124
131
|
const match = line.match(/^title:\s*(.*?)\s*$/);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
export type FeedbackSignalSource = "behavioral" | "explicit" | "self_critique";
|
|
3
|
+
export type FeedbackSignalValence = "positive" | "negative" | "neutral" | "correction";
|
|
4
|
+
export type FeedbackScopeType = "user" | "agent" | "agent_slug" | "channel" | "task" | "integration";
|
|
5
|
+
export type FeedbackActionKind = "notification" | "agent_execution" | "vault_write" | "dm_reply";
|
|
6
|
+
export interface FeedbackSignalRow {
|
|
7
|
+
id: number;
|
|
8
|
+
created_at: string;
|
|
9
|
+
source: FeedbackSignalSource;
|
|
10
|
+
valence: FeedbackSignalValence | null;
|
|
11
|
+
scope_type: FeedbackScopeType;
|
|
12
|
+
scope_ref: string | null;
|
|
13
|
+
action_kind: FeedbackActionKind | null;
|
|
14
|
+
action_ref: string | null;
|
|
15
|
+
agent_id: string | null;
|
|
16
|
+
summary: string;
|
|
17
|
+
evidence_json: string | null;
|
|
18
|
+
consumed_at: string | null;
|
|
19
|
+
lesson_ref: string | null;
|
|
20
|
+
}
|
|
21
|
+
export interface RecordFeedbackSignalParams {
|
|
22
|
+
source: FeedbackSignalSource;
|
|
23
|
+
valence?: FeedbackSignalValence | null;
|
|
24
|
+
scopeType: FeedbackScopeType;
|
|
25
|
+
scopeRef?: string | null;
|
|
26
|
+
actionKind?: FeedbackActionKind | null;
|
|
27
|
+
actionRef?: string | null;
|
|
28
|
+
agentId?: string | null;
|
|
29
|
+
summary: string;
|
|
30
|
+
evidence?: unknown;
|
|
31
|
+
}
|
|
32
|
+
export interface RecentFeedbackSignalLookup {
|
|
33
|
+
scopeType: FeedbackScopeType;
|
|
34
|
+
scopeRef?: string | null;
|
|
35
|
+
summary: string;
|
|
36
|
+
withinSeconds: number;
|
|
37
|
+
}
|
|
38
|
+
export declare function recordFeedbackSignal(db: Database.Database, params: RecordFeedbackSignalParams): number;
|
|
39
|
+
export declare function findRecentFeedbackSignal(db: Database.Database, params: RecentFeedbackSignalLookup): FeedbackSignalRow | null;
|
|
40
|
+
export declare function hasFeedbackSignalForAction(db: Database.Database, params: {
|
|
41
|
+
source: FeedbackSignalSource;
|
|
42
|
+
actionKind: FeedbackActionKind;
|
|
43
|
+
actionRef: string;
|
|
44
|
+
valence?: FeedbackSignalValence | null;
|
|
45
|
+
userReaction?: string;
|
|
46
|
+
}): boolean;
|
|
47
|
+
export declare function getPendingFeedbackSignals(db: Database.Database, params?: {
|
|
48
|
+
scopeType?: FeedbackScopeType;
|
|
49
|
+
scopeRef?: string | null;
|
|
50
|
+
limit?: number;
|
|
51
|
+
offset?: number;
|
|
52
|
+
}): FeedbackSignalRow[];
|
|
53
|
+
/**
|
|
54
|
+
* Count unconsumed signals, optionally narrowed to one scope type. Drives the
|
|
55
|
+
* `GET /api/feedback/lessons` "N signals awaiting tonight's consolidation"
|
|
56
|
+
* health figure (FEEDBACK_LEARNING_LOOP_DESIGN.md §9 Phase 5) without loading
|
|
57
|
+
* the rows. Uses the same `consumed_at IS NULL` partial index as
|
|
58
|
+
* {@link getPendingFeedbackSignals}.
|
|
59
|
+
*/
|
|
60
|
+
export declare function countPendingFeedbackSignals(db: Database.Database, params?: {
|
|
61
|
+
scopeType?: FeedbackScopeType;
|
|
62
|
+
}): number;
|
|
63
|
+
export declare function consumeFeedbackSignals(db: Database.Database, ids: number[], lessonRef?: string | null): {
|
|
64
|
+
consumed: number;
|
|
65
|
+
notFound: number[];
|
|
66
|
+
};
|
|
67
|
+
export declare function sweepConsumedFeedbackSignals(db: Database.Database, cutoff: string): number;
|
|
68
|
+
/**
|
|
69
|
+
* Compute the retention cutoff ISO timestamp for
|
|
70
|
+
* {@link sweepConsumedFeedbackSignals} from the `feedbackSignalRetentionDays`
|
|
71
|
+
* knob. Returns `null` when the knob is missing or non-finite (NaN / Infinity),
|
|
72
|
+
* so the caller degrades to "skip the sweep" instead of throwing on
|
|
73
|
+
* `new Date(NaN).toISOString()` and abandoning the whole nightly consolidation
|
|
74
|
+
* (FEEDBACK_LEARNING_LOOP_DESIGN.md §11 v1.3 robustness). `nowMs` is injected so
|
|
75
|
+
* the math is deterministically testable.
|
|
76
|
+
*/
|
|
77
|
+
export declare function feedbackRetentionCutoff(retentionDays: number | undefined, nowMs: number): string | null;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
export function recordFeedbackSignal(db, params) {
|
|
2
|
+
const evidenceJson = params.evidence === undefined ? "{}" : JSON.stringify(params.evidence);
|
|
3
|
+
const row = db
|
|
4
|
+
.prepare(`INSERT INTO feedback_signals (
|
|
5
|
+
source,
|
|
6
|
+
valence,
|
|
7
|
+
scope_type,
|
|
8
|
+
scope_ref,
|
|
9
|
+
action_kind,
|
|
10
|
+
action_ref,
|
|
11
|
+
agent_id,
|
|
12
|
+
summary,
|
|
13
|
+
evidence_json
|
|
14
|
+
)
|
|
15
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
16
|
+
RETURNING id`)
|
|
17
|
+
.get(params.source, params.valence ?? null, params.scopeType, params.scopeRef ?? null, params.actionKind ?? null, params.actionRef ?? null, params.agentId ?? null, params.summary, evidenceJson);
|
|
18
|
+
return row.id;
|
|
19
|
+
}
|
|
20
|
+
export function findRecentFeedbackSignal(db, params) {
|
|
21
|
+
const row = db
|
|
22
|
+
.prepare(`SELECT *
|
|
23
|
+
FROM feedback_signals
|
|
24
|
+
WHERE scope_type = ?
|
|
25
|
+
AND COALESCE(scope_ref, '') = COALESCE(?, '')
|
|
26
|
+
AND summary = ?
|
|
27
|
+
AND datetime(created_at) >= datetime('now', '-' || ? || ' seconds')
|
|
28
|
+
ORDER BY datetime(created_at) DESC, id DESC
|
|
29
|
+
LIMIT 1`)
|
|
30
|
+
.get(params.scopeType, params.scopeRef ?? null, params.summary, Math.max(0, Math.floor(params.withinSeconds)));
|
|
31
|
+
return row ?? null;
|
|
32
|
+
}
|
|
33
|
+
export function hasFeedbackSignalForAction(db, params) {
|
|
34
|
+
const where = [
|
|
35
|
+
"source = ?",
|
|
36
|
+
"action_kind = ?",
|
|
37
|
+
"action_ref = ?",
|
|
38
|
+
];
|
|
39
|
+
const values = [params.source, params.actionKind, params.actionRef];
|
|
40
|
+
if (params.valence !== undefined) {
|
|
41
|
+
where.push(params.valence === null ? "valence IS NULL" : "valence = ?");
|
|
42
|
+
if (params.valence !== null)
|
|
43
|
+
values.push(params.valence);
|
|
44
|
+
}
|
|
45
|
+
if (params.userReaction !== undefined) {
|
|
46
|
+
where.push("json_extract(evidence_json, '$.userReaction') = ?");
|
|
47
|
+
values.push(params.userReaction);
|
|
48
|
+
}
|
|
49
|
+
const row = db
|
|
50
|
+
.prepare(`SELECT 1 AS present
|
|
51
|
+
FROM feedback_signals
|
|
52
|
+
WHERE ${where.join(" AND ")}
|
|
53
|
+
LIMIT 1`)
|
|
54
|
+
.get(...values);
|
|
55
|
+
return row !== undefined;
|
|
56
|
+
}
|
|
57
|
+
export function getPendingFeedbackSignals(db, params = {}) {
|
|
58
|
+
const where = ["consumed_at IS NULL"];
|
|
59
|
+
const values = [];
|
|
60
|
+
if (params.scopeType !== undefined) {
|
|
61
|
+
where.push("scope_type = ?");
|
|
62
|
+
values.push(params.scopeType);
|
|
63
|
+
}
|
|
64
|
+
if (params.scopeRef !== undefined) {
|
|
65
|
+
where.push("COALESCE(scope_ref, '') = COALESCE(?, '')");
|
|
66
|
+
values.push(params.scopeRef);
|
|
67
|
+
}
|
|
68
|
+
const limit = Math.min(Math.max(params.limit ?? 100, 1), 500);
|
|
69
|
+
const offset = Math.max(params.offset ?? 0, 0);
|
|
70
|
+
values.push(limit, offset);
|
|
71
|
+
return db
|
|
72
|
+
.prepare(`SELECT *
|
|
73
|
+
FROM feedback_signals
|
|
74
|
+
WHERE ${where.join(" AND ")}
|
|
75
|
+
ORDER BY datetime(created_at) ASC, id ASC
|
|
76
|
+
LIMIT ? OFFSET ?`)
|
|
77
|
+
.all(...values);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Count unconsumed signals, optionally narrowed to one scope type. Drives the
|
|
81
|
+
* `GET /api/feedback/lessons` "N signals awaiting tonight's consolidation"
|
|
82
|
+
* health figure (FEEDBACK_LEARNING_LOOP_DESIGN.md §9 Phase 5) without loading
|
|
83
|
+
* the rows. Uses the same `consumed_at IS NULL` partial index as
|
|
84
|
+
* {@link getPendingFeedbackSignals}.
|
|
85
|
+
*/
|
|
86
|
+
export function countPendingFeedbackSignals(db, params = {}) {
|
|
87
|
+
const where = ["consumed_at IS NULL"];
|
|
88
|
+
const values = [];
|
|
89
|
+
if (params.scopeType !== undefined) {
|
|
90
|
+
where.push("scope_type = ?");
|
|
91
|
+
values.push(params.scopeType);
|
|
92
|
+
}
|
|
93
|
+
const row = db
|
|
94
|
+
.prepare(`SELECT COUNT(*) AS n
|
|
95
|
+
FROM feedback_signals
|
|
96
|
+
WHERE ${where.join(" AND ")}`)
|
|
97
|
+
.get(...values);
|
|
98
|
+
return row.n;
|
|
99
|
+
}
|
|
100
|
+
export function consumeFeedbackSignals(db, ids, lessonRef) {
|
|
101
|
+
if (ids.length === 0)
|
|
102
|
+
return { consumed: 0, notFound: [] };
|
|
103
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
104
|
+
const existing = db
|
|
105
|
+
.prepare(`SELECT id
|
|
106
|
+
FROM feedback_signals
|
|
107
|
+
WHERE id IN (${placeholders}) AND consumed_at IS NULL`)
|
|
108
|
+
.all(...ids);
|
|
109
|
+
const existingIds = new Set(existing.map((row) => row.id));
|
|
110
|
+
const notFound = ids.filter((id) => !existingIds.has(id));
|
|
111
|
+
if (existingIds.size === 0)
|
|
112
|
+
return { consumed: 0, notFound };
|
|
113
|
+
const updateIds = Array.from(existingIds);
|
|
114
|
+
const updatePlaceholders = updateIds.map(() => "?").join(",");
|
|
115
|
+
const consumed = db
|
|
116
|
+
.prepare(`UPDATE feedback_signals
|
|
117
|
+
SET consumed_at = CURRENT_TIMESTAMP, lesson_ref = COALESCE(?, lesson_ref)
|
|
118
|
+
WHERE id IN (${updatePlaceholders}) AND consumed_at IS NULL`)
|
|
119
|
+
.run(lessonRef ?? null, ...updateIds).changes;
|
|
120
|
+
return { consumed, notFound };
|
|
121
|
+
}
|
|
122
|
+
export function sweepConsumedFeedbackSignals(db, cutoff) {
|
|
123
|
+
return db
|
|
124
|
+
.prepare(`DELETE FROM feedback_signals
|
|
125
|
+
WHERE consumed_at IS NOT NULL
|
|
126
|
+
AND datetime(consumed_at) < datetime(?)`)
|
|
127
|
+
.run(cutoff).changes;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Compute the retention cutoff ISO timestamp for
|
|
131
|
+
* {@link sweepConsumedFeedbackSignals} from the `feedbackSignalRetentionDays`
|
|
132
|
+
* knob. Returns `null` when the knob is missing or non-finite (NaN / Infinity),
|
|
133
|
+
* so the caller degrades to "skip the sweep" instead of throwing on
|
|
134
|
+
* `new Date(NaN).toISOString()` and abandoning the whole nightly consolidation
|
|
135
|
+
* (FEEDBACK_LEARNING_LOOP_DESIGN.md §11 v1.3 robustness). `nowMs` is injected so
|
|
136
|
+
* the math is deterministically testable.
|
|
137
|
+
*/
|
|
138
|
+
export function feedbackRetentionCutoff(retentionDays, nowMs) {
|
|
139
|
+
if (typeof retentionDays !== "number" || !Number.isFinite(retentionDays)) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const retentionMs = retentionDays * 24 * 60 * 60 * 1000;
|
|
143
|
+
return new Date(nowMs - retentionMs).toISOString();
|
|
144
|
+
}
|
package/dist/db/migrations.js
CHANGED
|
@@ -379,6 +379,56 @@ export const MIGRATIONS = [
|
|
|
379
379
|
db.exec("UPDATE agent_schedule SET task_prompt = task_description WHERE task_prompt IS NULL");
|
|
380
380
|
},
|
|
381
381
|
},
|
|
382
|
+
{
|
|
383
|
+
id: "0009-today-refresh-budget-bump",
|
|
384
|
+
description: "(v0.1.9→next) — raise the routine.today_refresh per-turn budget "
|
|
385
|
+
+ "ceiling from the seeded $0.30 to $0.50 for upgrading installs still "
|
|
386
|
+
+ "on the seeded default. The drift-triggered today.md ## User Schedule "
|
|
387
|
+
+ "refresh reads up to 200 pending calendar observations and retries the "
|
|
388
|
+
+ "section PATCH up to 3x with 30s backoffs when the morning-routine lock "
|
|
389
|
+
+ "is held; a busy-calendar drift compounded by that retry loop tipped a "
|
|
390
|
+
+ "real run past $0.30 and surfaced BackendQuotaError(max_budget_usd) with "
|
|
391
|
+
+ "no fallback (claude is the only binding) — the prior $0.10→$0.30 bump "
|
|
392
|
+
+ "did not hold. Backend-aware: applyDefaultPresets stores the post-hoc-"
|
|
393
|
+
+ "scaled budget (codex/gemini medium x1.5), so the OLD default is $0.30 "
|
|
394
|
+
+ "on claude/opencode and $0.45 on codex/gemini, and the NEW default is "
|
|
395
|
+
+ "the $0.50 base scaled the same way -> $0.50 / $0.75. Fresh installs "
|
|
396
|
+
+ "already get the new value from the schema seed + the per-process "
|
|
397
|
+
+ "envelope-overrides map; this migration only touches pre-existing "
|
|
398
|
+
+ "installs. Gated so it ONLY moves preset rows still at the OLD per-"
|
|
399
|
+
+ "backend default — operator-pinned rows (updated_by='user') and rows "
|
|
400
|
+
+ "already at a custom value are left untouched. Idempotent: after the "
|
|
401
|
+
+ "bump no row sits in the old band, and the recorded id short-circuits a "
|
|
402
|
+
+ "re-run anyway.",
|
|
403
|
+
up(db) {
|
|
404
|
+
// Empty-DB safety (e.g. the runner's own unit tests run on a bare
|
|
405
|
+
// :memory: db): if applySchema never ran, the table is absent — the
|
|
406
|
+
// runner still records the id so a later boot does not re-evaluate.
|
|
407
|
+
if (!tableExists(db, "process_backend_config"))
|
|
408
|
+
return;
|
|
409
|
+
// The NEW per-backend value mirrors what `resolveDefaultBindingFor`
|
|
410
|
+
// now produces for routine.today_refresh: the $0.50 base x the medium
|
|
411
|
+
// post-hoc factor (1.5 for codex/gemini, 1.0 for claude/opencode). The
|
|
412
|
+
// 0.75 literal is that product at migration time — a migration is a
|
|
413
|
+
// point-in-time snapshot, so the literal is correct even if the factor
|
|
414
|
+
// later changes. The old-default bands ([0.29,0.31] / [0.44,0.46]) keep
|
|
415
|
+
// us from clobbering a row already moved to a custom value while still
|
|
416
|
+
// tolerating float dust.
|
|
417
|
+
db.prepare(`UPDATE process_backend_config
|
|
418
|
+
SET max_budget_usd = CASE
|
|
419
|
+
WHEN main_backend IN ('codex', 'gemini') THEN 0.75
|
|
420
|
+
ELSE 0.5
|
|
421
|
+
END
|
|
422
|
+
WHERE process_key = 'routine.today_refresh'
|
|
423
|
+
AND updated_by = 'preset'
|
|
424
|
+
AND (
|
|
425
|
+
(main_backend IN ('codex', 'gemini')
|
|
426
|
+
AND max_budget_usd >= 0.44 AND max_budget_usd <= 0.46)
|
|
427
|
+
OR (main_backend NOT IN ('codex', 'gemini')
|
|
428
|
+
AND max_budget_usd >= 0.29 AND max_budget_usd <= 0.31)
|
|
429
|
+
)`).run();
|
|
430
|
+
},
|
|
431
|
+
},
|
|
382
432
|
];
|
|
383
433
|
/**
|
|
384
434
|
* Ensure the `schema_migrations` bookkeeping table exists, then apply every
|
package/dist/db/schema.js
CHANGED
|
@@ -382,6 +382,34 @@ CREATE TABLE IF NOT EXISTS notification_log (
|
|
|
382
382
|
CREATE INDEX IF NOT EXISTS idx_notification_dispatch
|
|
383
383
|
ON notification_log(dispatch_id, platform);
|
|
384
384
|
|
|
385
|
+
-- ── Feedback Learning Loop (FEEDBACK_LEARNING_LOOP_DESIGN.md Phase 1) ────────
|
|
386
|
+
--
|
|
387
|
+
-- Typed, append-only raw feedback signals. This is the structured sibling of
|
|
388
|
+
-- identity/profile.md "Raw Signals": behavioral notification outcomes, explicit
|
|
389
|
+
-- owner directives, and review-routine self-critique all land here before the
|
|
390
|
+
-- nightly consolidation pass promotes them into scoped lessons.
|
|
391
|
+
CREATE TABLE IF NOT EXISTS feedback_signals (
|
|
392
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
393
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
394
|
+
source TEXT NOT NULL
|
|
395
|
+
CHECK (source IN ('behavioral', 'explicit', 'self_critique')),
|
|
396
|
+
valence TEXT
|
|
397
|
+
CHECK (valence IN ('positive', 'negative', 'neutral', 'correction')),
|
|
398
|
+
scope_type TEXT NOT NULL
|
|
399
|
+
CHECK (scope_type IN ('user', 'agent', 'agent_slug', 'channel', 'task', 'integration')),
|
|
400
|
+
scope_ref TEXT,
|
|
401
|
+
action_kind TEXT,
|
|
402
|
+
action_ref TEXT,
|
|
403
|
+
agent_id TEXT,
|
|
404
|
+
summary TEXT NOT NULL,
|
|
405
|
+
evidence_json JSON DEFAULT '{}',
|
|
406
|
+
consumed_at TIMESTAMP,
|
|
407
|
+
lesson_ref TEXT
|
|
408
|
+
);
|
|
409
|
+
CREATE INDEX IF NOT EXISTS idx_feedback_unconsumed
|
|
410
|
+
ON feedback_signals(consumed_at, scope_type, scope_ref)
|
|
411
|
+
WHERE consumed_at IS NULL;
|
|
412
|
+
|
|
385
413
|
CREATE TABLE IF NOT EXISTS owner_channels (
|
|
386
414
|
platform TEXT PRIMARY KEY,
|
|
387
415
|
sender_id TEXT,
|
|
@@ -2137,12 +2165,21 @@ VALUES
|
|
|
2137
2165
|
('routine.morning_routine_today', 'claude', '${DEFAULT_CLAUDE_MEDIUM_MODEL}', 50, 1.50, 'preset'),
|
|
2138
2166
|
('routine.morning_routine_journal', 'claude', '${DEFAULT_CLAUDE_LITE_MODEL}', 20, 0.30, 'preset'),
|
|
2139
2167
|
('routine.hourly_check', 'claude', '${DEFAULT_CLAUDE_MEDIUM_MODEL}', 50, 1.00, 'preset'),
|
|
2140
|
-
-- $0.
|
|
2141
|
-
-- in 4 turns
|
|
2142
|
-
--
|
|
2143
|
-
--
|
|
2144
|
-
--
|
|
2145
|
-
(
|
|
2168
|
+
-- $0.50 budget: a typical drift-triggered refresh on Sonnet runs ~$0.10
|
|
2169
|
+
-- in 4 turns, but a busy-calendar drift (many/large pending calendar
|
|
2170
|
+
-- observations read via the task-flow GET limit=200) compounded by a
|
|
2171
|
+
-- 409 morning-lock retry loop (3x with 30s backoffs) tipped a real run
|
|
2172
|
+
-- past the prior $0.30 cap and surfaced as BackendQuotaError(max_budget_usd)
|
|
2173
|
+
-- with no fallback (claude is the only binding). The previous $0.10→$0.30
|
|
2174
|
+
-- bump did not hold. Realigned to $0.50 — the medium-tier 20-turn peer
|
|
2175
|
+
-- dashboard.docs_qa value, and well under the superset
|
|
2176
|
+
-- routine.morning_routine_today ($1.50), which writes the full today.md
|
|
2177
|
+
-- from the same calendar data. A third trip should drive a
|
|
2178
|
+
-- work-reduction fix (tighter GET limit / cheaper 409 backoff), not
|
|
2179
|
+
-- another blind bump. Bumped for upgrading installs by migration 0009.
|
|
2180
|
+
-- Keep in lock-step with ENVELOPE_OVERRIDES_BY_PROCESS_KEY in
|
|
2181
|
+
-- plan-presets.ts.
|
|
2182
|
+
('routine.today_refresh', 'claude', '${DEFAULT_CLAUDE_MEDIUM_MODEL}', 20, 0.50, 'preset'),
|
|
2146
2183
|
('routine.evening_review', 'claude', '${DEFAULT_CLAUDE_MEDIUM_MODEL}', 50, 1.00, 'preset'),
|
|
2147
2184
|
('routine.weekly_review', 'claude', '${DEFAULT_CLAUDE_MEDIUM_MODEL}', 50, 1.00, 'preset'),
|
|
2148
2185
|
-- routine.monthly_review: row seeded but the routine is gated OFF by
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* are matched as shell-command globs, Read/Write/Edit arguments as path
|
|
37
37
|
* globs.
|
|
38
38
|
*/
|
|
39
|
-
export declare const ALWAYS_DISALLOWED_TOOLS: readonly ["Bash(rm -rf *)", "Bash(rm -rf /*)", "Bash(rm -rf ~*)", "Bash(rm -rf .*)", "Bash(rm -r *)", "Bash(rm -rf*)", "Bash(rm -rv*)", "Bash(rm -ri*)", "Bash(rm -rd*)", "Bash(rm -rI*)", "Bash(rm -fr*)", "Bash(rm -vr*)", "Bash(rm -ir*)", "Bash(rm -dr*)", "Bash(rm -Ir*)", "Bash(rm -R*)", "Bash(rm -fR*)", "Bash(rm -vR*)", "Bash(rm -iR*)", "Bash(rm -dR*)", "Bash(rm -IR*)", "Bash(rm --recursive*)", "Bash(rm --force --recursive*)", "Bash(rm --recursive --force*)", "Bash(sudo *)", "Bash(doas *)", "Bash(su *)", "Bash(curl * | sh*)", "Bash(curl * | bash*)", "Bash(wget * | sh*)", "Bash(wget * | bash*)", "Bash(bash <(*)*)", "Bash(sh <(*)*)", "Bash(bash<*)", "Bash(sh<*)", "Bash(eval *)", "Bash(source *)", "Bash(security *)", "Bash(secret-tool *)", "Bash(cmdkey *)", "Bash(certutil *)", "Bash(rundll32.exe *)", "Read(.env)", "Read(.env.*)", "Read(**/.env)", "Read(**/.env.*)", "Read(id_rsa*)", "Read(id_ed25519*)", "Read(~/.ssh/**)", "Read(~/.gnupg/**)", "Read(~/.aws/**)", "Read(~/.config/gcloud/**)", "Read(~/.config/gh/hosts.yml)", "Read(~/.netrc)", "Read(~/Library/Keychains/**)", "Read(~/.local/share/keyrings/**)", "Read(~/.personal-agent/backups/**)", "Read(~/.personal-agent/whatsapp/auth/**)", "Read(~/.personal-agent/secrets/**)", "Write(.env)", "Edit(.env)", "Write(.env.*)", "Edit(.env.*)", "Write(**/.env)", "Edit(**/.env)", "Write(**/.env.*)", "Edit(**/.env.*)", "Write(id_rsa*)", "Edit(id_rsa*)", "Write(id_ed25519*)", "Edit(id_ed25519*)", "Write(~/.ssh/**)", "Edit(~/.ssh/**)", "Write(~/.gnupg/**)", "Edit(~/.gnupg/**)", "Write(~/.aws/**)", "Edit(~/.aws/**)", "Write(~/.config/gcloud/**)", "Edit(~/.config/gcloud/**)", "Write(~/.config/gh/hosts.yml)", "Edit(~/.config/gh/hosts.yml)", "Write(~/.netrc)", "Edit(~/.netrc)", "Write(~/Library/Keychains/**)", "Edit(~/Library/Keychains/**)", "Write(~/.local/share/keyrings/**)", "Edit(~/.local/share/keyrings/**)", "Write(~/.personal-agent/backups/**)", "Edit(~/.personal-agent/backups/**)", "Write(~/.personal-agent/whatsapp/auth/**)", "Edit(~/.personal-agent/whatsapp/auth/**)", "Write(~/.personal-agent/secrets/**)", "Edit(~/.personal-agent/secrets/**)", "Bash(sqlite3 *)", "Bash(cp ~/Library/Application Support/Google/Chrome/*)", "Bash(cp ~/Library/Application Support/Chromium/*)", "Bash(cp ~/Library/Application Support/Microsoft Edge/*)", "Bash(cp ~/Library/Application Support/BraveSoftware/*)", "Bash(cp ~/Library/Application Support/Comet/*)", "Bash(cp ~/Library/Application Support/Perplexity Comet/*)", "Bash(cp ~/Library/Application Support/com.openai.atlas/*)", "Bash(cp ~/.config/google-chrome/*)", "Bash(cp ~/.config/chromium/*)", "Bash(cp ~/.config/microsoft-edge/*)", "Bash(cp ~/.config/BraveSoftware/*)", "Bash(cp ~/.config/Comet/*)", "Bash(cp ~/.var/app/com.google.Chrome/*)", "Bash(cp /mnt/c/Users/*)", "Bash(curl file://*)", "Read(~/Library/Application Support/Google/Chrome/**)", "Read(~/Library/Application Support/Chromium/**)", "Read(~/Library/Application Support/Microsoft Edge/**)", "Read(~/Library/Application Support/BraveSoftware/**)", "Read(~/Library/Application Support/Comet/**)", "Read(~/Library/Application Support/Perplexity Comet/**)", "Read(~/Library/Application Support/com.openai.atlas/**)", "Read(~/.config/google-chrome/**)", "Read(~/.config/chromium/**)", "Read(~/.config/microsoft-edge/**)", "Read(~/.config/BraveSoftware/**)", "Read(~/.config/Comet/**)", "Read(~/.var/app/com.google.Chrome/**)", "Read(/mnt/c/Users/**)", "Write(~/Library/Application Support/Google/Chrome/**)", "Edit(~/Library/Application Support/Google/Chrome/**)", "Write(~/Library/Application Support/Chromium/**)", "Edit(~/Library/Application Support/Chromium/**)", "Write(~/Library/Application Support/Microsoft Edge/**)", "Edit(~/Library/Application Support/Microsoft Edge/**)", "Write(~/Library/Application Support/BraveSoftware/**)", "Edit(~/Library/Application Support/BraveSoftware/**)", "Write(~/Library/Application Support/Comet/**)", "Edit(~/Library/Application Support/Comet/**)", "Write(~/Library/Application Support/Perplexity Comet/**)", "Edit(~/Library/Application Support/Perplexity Comet/**)", "Write(~/Library/Application Support/com.openai.atlas/**)", "Edit(~/Library/Application Support/com.openai.atlas/**)", "Write(~/.config/google-chrome/**)", "Edit(~/.config/google-chrome/**)", "Write(~/.config/chromium/**)", "Edit(~/.config/chromium/**)", "Write(~/.config/microsoft-edge/**)", "Edit(~/.config/microsoft-edge/**)", "Write(~/.config/BraveSoftware/**)", "Edit(~/.config/BraveSoftware/**)", "Write(~/.config/Comet/**)", "Edit(~/.config/Comet/**)", "Write(~/.var/app/com.google.Chrome/**)", "Edit(~/.var/app/com.google.Chrome/**)", "Write(/mnt/c/Users/**)", "Edit(/mnt/c/Users/**)", "Bash(cp ~/.personal-agent/chromium-*)", "Bash(mv ~/.personal-agent/chromium-*)", "Bash(tar ~/.personal-agent/chromium-*)", "Bash(zip ~/.personal-agent/chromium-*)", "Bash(rsync ~/.personal-agent/chromium-*)", "Bash(cp $HOME/.personal-agent/chromium-*)", "Bash(mv $HOME/.personal-agent/chromium-*)", "Bash(tar $HOME/.personal-agent/chromium-*)", "Bash(zip $HOME/.personal-agent/chromium-*)", "Bash(rsync $HOME/.personal-agent/chromium-*)", "Read(~/.personal-agent/chromium-sync/**)", "Read(~/.personal-agent/chromium-automation/**)", "Read(~/.personal-agent/chromium-automation-anon/**)", "Read(~/.personal-agent/chromium-automation-auth/**)", "Read(~/.personal-agent/chromium-automation-purchase/**)", "Write(~/.personal-agent/chromium-sync/**)", "Edit(~/.personal-agent/chromium-sync/**)", "Write(~/.personal-agent/chromium-automation/**)", "Edit(~/.personal-agent/chromium-automation/**)", "Write(~/.personal-agent/chromium-automation-anon/**)", "Edit(~/.personal-agent/chromium-automation-anon/**)", "Write(~/.personal-agent/chromium-automation-auth/**)", "Edit(~/.personal-agent/chromium-automation-auth/**)", "Write(~/.personal-agent/chromium-automation-purchase/**)", "Edit(~/.personal-agent/chromium-automation-purchase/**)", "CronCreate", "CronList", "CronDelete", "RemoteTrigger", "PushNotification"];
|
|
39
|
+
export declare const ALWAYS_DISALLOWED_TOOLS: readonly ["Bash(rm -rf *)", "Bash(rm -rf /*)", "Bash(rm -rf ~*)", "Bash(rm -rf .*)", "Bash(rm -r *)", "Bash(rm -rf*)", "Bash(rm -rv*)", "Bash(rm -ri*)", "Bash(rm -rd*)", "Bash(rm -rI*)", "Bash(rm -fr*)", "Bash(rm -vr*)", "Bash(rm -ir*)", "Bash(rm -dr*)", "Bash(rm -Ir*)", "Bash(rm -R*)", "Bash(rm -fR*)", "Bash(rm -vR*)", "Bash(rm -iR*)", "Bash(rm -dR*)", "Bash(rm -IR*)", "Bash(rm --recursive*)", "Bash(rm --force --recursive*)", "Bash(rm --recursive --force*)", "Bash(sudo *)", "Bash(doas *)", "Bash(su *)", "Bash(curl * | sh*)", "Bash(curl * | bash*)", "Bash(wget * | sh*)", "Bash(wget * | bash*)", "Bash(bash <(*)*)", "Bash(sh <(*)*)", "Bash(bash<*)", "Bash(sh<*)", "Bash(eval *)", "Bash(source *)", "Bash(security *)", "Bash(secret-tool *)", "Bash(cmdkey *)", "Bash(certutil *)", "Bash(rundll32.exe *)", "Read(.env)", "Read(.env.*)", "Read(**/.env)", "Read(**/.env.*)", "Read(id_rsa*)", "Read(id_ed25519*)", "Read(~/.ssh/**)", "Read(~/.gnupg/**)", "Read(~/.aws/**)", "Read(~/.config/gcloud/**)", "Read(~/.config/gh/hosts.yml)", "Read(~/.netrc)", "Read(~/Library/Keychains/**)", "Read(~/.local/share/keyrings/**)", "Read(~/.personal-agent/backups/**)", "Read(~/.personal-agent/whatsapp/auth/**)", "Read(~/.personal-agent/secrets/**)", "Read(~/.claude/.credentials.json)", "Read(~/.claude.json)", "Read(~/.codex/auth.json)", "Read(~/.gemini/gemini-credentials.json)", "Read(~/.gemini/oauth_creds.json)", "Read(~/.config/anthropic/**)", "Write(.env)", "Edit(.env)", "Write(.env.*)", "Edit(.env.*)", "Write(**/.env)", "Edit(**/.env)", "Write(**/.env.*)", "Edit(**/.env.*)", "Write(id_rsa*)", "Edit(id_rsa*)", "Write(id_ed25519*)", "Edit(id_ed25519*)", "Write(~/.ssh/**)", "Edit(~/.ssh/**)", "Write(~/.gnupg/**)", "Edit(~/.gnupg/**)", "Write(~/.aws/**)", "Edit(~/.aws/**)", "Write(~/.config/gcloud/**)", "Edit(~/.config/gcloud/**)", "Write(~/.config/gh/hosts.yml)", "Edit(~/.config/gh/hosts.yml)", "Write(~/.netrc)", "Edit(~/.netrc)", "Write(~/Library/Keychains/**)", "Edit(~/Library/Keychains/**)", "Write(~/.local/share/keyrings/**)", "Edit(~/.local/share/keyrings/**)", "Write(~/.personal-agent/backups/**)", "Edit(~/.personal-agent/backups/**)", "Write(~/.personal-agent/whatsapp/auth/**)", "Edit(~/.personal-agent/whatsapp/auth/**)", "Write(~/.personal-agent/secrets/**)", "Edit(~/.personal-agent/secrets/**)", "Write(~/.claude/.credentials.json)", "Edit(~/.claude/.credentials.json)", "Write(~/.claude.json)", "Edit(~/.claude.json)", "Write(~/.codex/auth.json)", "Edit(~/.codex/auth.json)", "Write(~/.gemini/gemini-credentials.json)", "Edit(~/.gemini/gemini-credentials.json)", "Write(~/.gemini/oauth_creds.json)", "Edit(~/.gemini/oauth_creds.json)", "Write(~/.config/anthropic/**)", "Edit(~/.config/anthropic/**)", "Bash(sqlite3 *)", "Bash(cp ~/Library/Application Support/Google/Chrome/*)", "Bash(cp ~/Library/Application Support/Chromium/*)", "Bash(cp ~/Library/Application Support/Microsoft Edge/*)", "Bash(cp ~/Library/Application Support/BraveSoftware/*)", "Bash(cp ~/Library/Application Support/Comet/*)", "Bash(cp ~/Library/Application Support/Perplexity Comet/*)", "Bash(cp ~/Library/Application Support/com.openai.atlas/*)", "Bash(cp ~/.config/google-chrome/*)", "Bash(cp ~/.config/chromium/*)", "Bash(cp ~/.config/microsoft-edge/*)", "Bash(cp ~/.config/BraveSoftware/*)", "Bash(cp ~/.config/Comet/*)", "Bash(cp ~/.var/app/com.google.Chrome/*)", "Bash(cp /mnt/c/Users/*)", "Bash(curl file://*)", "Read(~/Library/Application Support/Google/Chrome/**)", "Read(~/Library/Application Support/Chromium/**)", "Read(~/Library/Application Support/Microsoft Edge/**)", "Read(~/Library/Application Support/BraveSoftware/**)", "Read(~/Library/Application Support/Comet/**)", "Read(~/Library/Application Support/Perplexity Comet/**)", "Read(~/Library/Application Support/com.openai.atlas/**)", "Read(~/.config/google-chrome/**)", "Read(~/.config/chromium/**)", "Read(~/.config/microsoft-edge/**)", "Read(~/.config/BraveSoftware/**)", "Read(~/.config/Comet/**)", "Read(~/.var/app/com.google.Chrome/**)", "Read(/mnt/c/Users/**)", "Write(~/Library/Application Support/Google/Chrome/**)", "Edit(~/Library/Application Support/Google/Chrome/**)", "Write(~/Library/Application Support/Chromium/**)", "Edit(~/Library/Application Support/Chromium/**)", "Write(~/Library/Application Support/Microsoft Edge/**)", "Edit(~/Library/Application Support/Microsoft Edge/**)", "Write(~/Library/Application Support/BraveSoftware/**)", "Edit(~/Library/Application Support/BraveSoftware/**)", "Write(~/Library/Application Support/Comet/**)", "Edit(~/Library/Application Support/Comet/**)", "Write(~/Library/Application Support/Perplexity Comet/**)", "Edit(~/Library/Application Support/Perplexity Comet/**)", "Write(~/Library/Application Support/com.openai.atlas/**)", "Edit(~/Library/Application Support/com.openai.atlas/**)", "Write(~/.config/google-chrome/**)", "Edit(~/.config/google-chrome/**)", "Write(~/.config/chromium/**)", "Edit(~/.config/chromium/**)", "Write(~/.config/microsoft-edge/**)", "Edit(~/.config/microsoft-edge/**)", "Write(~/.config/BraveSoftware/**)", "Edit(~/.config/BraveSoftware/**)", "Write(~/.config/Comet/**)", "Edit(~/.config/Comet/**)", "Write(~/.var/app/com.google.Chrome/**)", "Edit(~/.var/app/com.google.Chrome/**)", "Write(/mnt/c/Users/**)", "Edit(/mnt/c/Users/**)", "Bash(cp ~/.personal-agent/chromium-*)", "Bash(mv ~/.personal-agent/chromium-*)", "Bash(tar ~/.personal-agent/chromium-*)", "Bash(zip ~/.personal-agent/chromium-*)", "Bash(rsync ~/.personal-agent/chromium-*)", "Bash(cp $HOME/.personal-agent/chromium-*)", "Bash(mv $HOME/.personal-agent/chromium-*)", "Bash(tar $HOME/.personal-agent/chromium-*)", "Bash(zip $HOME/.personal-agent/chromium-*)", "Bash(rsync $HOME/.personal-agent/chromium-*)", "Read(~/.personal-agent/chromium-sync/**)", "Read(~/.personal-agent/chromium-automation/**)", "Read(~/.personal-agent/chromium-automation-anon/**)", "Read(~/.personal-agent/chromium-automation-auth/**)", "Read(~/.personal-agent/chromium-automation-purchase/**)", "Write(~/.personal-agent/chromium-sync/**)", "Edit(~/.personal-agent/chromium-sync/**)", "Write(~/.personal-agent/chromium-automation/**)", "Edit(~/.personal-agent/chromium-automation/**)", "Write(~/.personal-agent/chromium-automation-anon/**)", "Edit(~/.personal-agent/chromium-automation-anon/**)", "Write(~/.personal-agent/chromium-automation-auth/**)", "Edit(~/.personal-agent/chromium-automation-auth/**)", "Write(~/.personal-agent/chromium-automation-purchase/**)", "Edit(~/.personal-agent/chromium-automation-purchase/**)", "CronCreate", "CronList", "CronDelete", "RemoteTrigger", "PushNotification"];
|
|
40
40
|
/**
|
|
41
41
|
* Structured classification of an attempted command or path against the
|
|
42
42
|
* absolute-block list, used by the §6.3 audit path.
|