flint-agent 1.14.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.
Files changed (171) hide show
  1. package/.env.example +108 -0
  2. package/CHANGELOG.md +55 -0
  3. package/FEATURES.md +298 -0
  4. package/LICENSE +21 -0
  5. package/README.md +435 -0
  6. package/bin/flint.js +47 -0
  7. package/config/classifier-prompt.md +218 -0
  8. package/config/models-curated.json +4 -0
  9. package/config/providers.json +74 -0
  10. package/package.json +92 -0
  11. package/patches/ink+6.8.0.patch +78 -0
  12. package/profiles/desktop.md +65 -0
  13. package/profiles/generic.md +20 -0
  14. package/profiles/marketer.md +20 -0
  15. package/profiles/profiles.json +34 -0
  16. package/profiles/ux-reviewer.md +25 -0
  17. package/src/agent/agent.js +1743 -0
  18. package/src/agent/auto.js +346 -0
  19. package/src/agent/backoff.js +143 -0
  20. package/src/agent/compression.js +310 -0
  21. package/src/agent/content-resolver.js +180 -0
  22. package/src/agent/flow-controller.js +309 -0
  23. package/src/agent/intent-manifest.js +231 -0
  24. package/src/agent/intent-timeout.js +46 -0
  25. package/src/agent/intent.js +633 -0
  26. package/src/agent/knowledge.js +114 -0
  27. package/src/agent/learning.js +180 -0
  28. package/src/agent/modes.js +187 -0
  29. package/src/agent/outcome-ask.js +91 -0
  30. package/src/agent/project-context.js +76 -0
  31. package/src/agent/prompt-budget.js +117 -0
  32. package/src/agent/reflection-extractor.js +140 -0
  33. package/src/agent/steering.js +86 -0
  34. package/src/agent/supervisor.js +430 -0
  35. package/src/agent/swap.js +443 -0
  36. package/src/agent/system-prompt.js +446 -0
  37. package/src/agent/time-stamp.js +48 -0
  38. package/src/agent/tool-guard.js +201 -0
  39. package/src/agent/toolcall-text.js +162 -0
  40. package/src/agent/usage.js +297 -0
  41. package/src/agent/vision.js +94 -0
  42. package/src/agent/watchdog.js +139 -0
  43. package/src/agent/workspace-changes.js +177 -0
  44. package/src/api/address.js +14 -0
  45. package/src/api/client.js +280 -0
  46. package/src/api/server.js +535 -0
  47. package/src/api/stream-pipe.js +113 -0
  48. package/src/app-state.js +39 -0
  49. package/src/bootstrap.js +501 -0
  50. package/src/bus/drain-loop.js +497 -0
  51. package/src/bus/index.js +270 -0
  52. package/src/bus/plugins.js +65 -0
  53. package/src/child-idle.js +14 -0
  54. package/src/cli.js +118 -0
  55. package/src/commands/commands.js +1297 -0
  56. package/src/commands/registry.js +132 -0
  57. package/src/components/App.js +491 -0
  58. package/src/components/CarefulMenu.js +145 -0
  59. package/src/components/HistoryWriter.js +86 -0
  60. package/src/components/LineInput.js +69 -0
  61. package/src/components/LiveZone.js +294 -0
  62. package/src/components/OverlayMenu.js +179 -0
  63. package/src/components/SystemPanel.js +156 -0
  64. package/src/components/Table.js +54 -0
  65. package/src/config.js +249 -0
  66. package/src/free-models.js +230 -0
  67. package/src/index.js +1111 -0
  68. package/src/input-handler.js +13 -0
  69. package/src/input-text.js +123 -0
  70. package/src/launcher.js +129 -0
  71. package/src/logging/api-log.js +95 -0
  72. package/src/logging/chat-log-follower.js +113 -0
  73. package/src/logging/chat-log.js +15 -0
  74. package/src/logging/log-collector.js +182 -0
  75. package/src/logging/logger.js +112 -0
  76. package/src/logging/tool-log.js +20 -0
  77. package/src/mcp-client.js +314 -0
  78. package/src/memory/conversation-digest.js +113 -0
  79. package/src/memory/extract-facts.js +98 -0
  80. package/src/memory/facts.js +181 -0
  81. package/src/memory/inbox.js +63 -0
  82. package/src/memory/markdown.js +38 -0
  83. package/src/memory/patterns.js +185 -0
  84. package/src/memory/project.js +66 -0
  85. package/src/memory/reflections.js +74 -0
  86. package/src/memory/retrieval.js +84 -0
  87. package/src/memory/rules.js +105 -0
  88. package/src/memory/session-facts.js +125 -0
  89. package/src/memory/skills.js +191 -0
  90. package/src/memory/sqlite-store.js +653 -0
  91. package/src/memory/store.js +208 -0
  92. package/src/memory/tools.js +196 -0
  93. package/src/memory/user-model.js +86 -0
  94. package/src/message-handler.js +775 -0
  95. package/src/model-check.js +218 -0
  96. package/src/plugins/loader.js +120 -0
  97. package/src/plugins/manager.js +88 -0
  98. package/src/production-env.js +22 -0
  99. package/src/profiles.js +42 -0
  100. package/src/providers/adapters/anthropic.js +270 -0
  101. package/src/providers/adapters/openai.js +120 -0
  102. package/src/providers/keys-dpapi.js +41 -0
  103. package/src/providers/keys-fallback.js +31 -0
  104. package/src/providers/keys.js +132 -0
  105. package/src/providers/models.js +154 -0
  106. package/src/providers/registry.js +56 -0
  107. package/src/providers/state.js +56 -0
  108. package/src/registry.js +96 -0
  109. package/src/restart.js +29 -0
  110. package/src/sandbox/backend.js +130 -0
  111. package/src/security/api-auth.js +132 -0
  112. package/src/security/audit.js +98 -0
  113. package/src/security/child-policy.js +41 -0
  114. package/src/security/command-guard.js +173 -0
  115. package/src/security/content-fence.js +250 -0
  116. package/src/security/content-validator.js +132 -0
  117. package/src/security/index.js +143 -0
  118. package/src/security/network-guard.js +126 -0
  119. package/src/security/pairing.js +180 -0
  120. package/src/security/path-guard.js +140 -0
  121. package/src/security/persona-guard.js +67 -0
  122. package/src/security/policies.js +452 -0
  123. package/src/security/safety-constants.js +34 -0
  124. package/src/security/watchdog.js +107 -0
  125. package/src/sessions.js +130 -0
  126. package/src/spend.js +97 -0
  127. package/src/startup-watchdog.js +59 -0
  128. package/src/stdio/args.js +71 -0
  129. package/src/stdio/guard.js +59 -0
  130. package/src/stdio/protocol.js +167 -0
  131. package/src/stdio/run.js +106 -0
  132. package/src/stdio/session.js +180 -0
  133. package/src/store/agent-slice.js +306 -0
  134. package/src/store/dataset-slice.js +73 -0
  135. package/src/store/index.js +22 -0
  136. package/src/store/process-slice.js +135 -0
  137. package/src/store/session-slice.js +191 -0
  138. package/src/store/ui-slice.js +119 -0
  139. package/src/tasks/db.js +184 -0
  140. package/src/tasks/queries.js +589 -0
  141. package/src/tools/agent-tools.js +473 -0
  142. package/src/tools/checkpoint.js +152 -0
  143. package/src/tools/command-approvals.js +180 -0
  144. package/src/tools/dataset.js +50 -0
  145. package/src/tools/filesystem.js +682 -0
  146. package/src/tools/inbox-tools.js +48 -0
  147. package/src/tools/mesh.js +135 -0
  148. package/src/tools/own-env.js +136 -0
  149. package/src/tools/permissions.js +681 -0
  150. package/src/tools/plugin-tools.js +123 -0
  151. package/src/tools/process-tools.js +595 -0
  152. package/src/tools/registry.js +307 -0
  153. package/src/tools/swap-tools.js +72 -0
  154. package/src/tools/system.js +662 -0
  155. package/src/tools/tasks.js +532 -0
  156. package/src/tools/tool-search.js +171 -0
  157. package/src/ui/header.js +140 -0
  158. package/src/ui/input-cursor.js +23 -0
  159. package/src/ui/last-line.js +25 -0
  160. package/src/ui/line-edit.js +135 -0
  161. package/src/ui/output.js +399 -0
  162. package/src/ui/paste-tokens.js +131 -0
  163. package/src/ui/prompt-attention.js +134 -0
  164. package/src/ui/render-options.js +13 -0
  165. package/src/ui/replay.js +94 -0
  166. package/src/ui/splash.js +49 -0
  167. package/src/ui/status-level.js +36 -0
  168. package/src/ui/tool-ledger.js +203 -0
  169. package/src/ui/window-title.js +150 -0
  170. package/src/update.js +205 -0
  171. package/system.md +63 -0
@@ -0,0 +1,180 @@
1
+ // [a]lways on a command prompt, remembered per project.
2
+ //
3
+ // Why this is its own file and not a field on the tool permission: the answer
4
+ // is not "this tool is fine", it is "this command is fine, in this project".
5
+ // Storing it as a tool-wide `allow` (which is what the permissions file did
6
+ // before) opens the tool everywhere — approving `npm test` in a scratch repo
7
+ // would then also approve it in the one holding production credentials, and the
8
+ // operator's answer was never about that.
9
+ //
10
+ // The key is a normalised (project, command) pair. Three separate narrowings,
11
+ // each of which has a way to be got wrong:
12
+ //
13
+ // project — the grant does not travel to another checkout
14
+ // argv[0] — the first word only, so `npm test` covers `npm test -- --watch`
15
+ // and not `npm publish`. A grant is about what a command DOES.
16
+ // argv[0] must be an actual executable — an absolute or relative path is
17
+ // used as-is, because `./scripts/deploy.sh` is one program with a
18
+ // specific meaning and flattening it to `deploy.sh` would match a
19
+ // different file elsewhere on PATH.
20
+ //
21
+ // Persisted to .permissions.json under a reserved key, next to the other
22
+ // grants the operator has given, so there is one file to look at. A grant that
23
+ // lives in memory is a prompt again after a restart, which is what the owner hit
24
+ // on 2026-09-29 and read as "[a]lways did nothing".
25
+
26
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
27
+ import path from "node:path";
28
+ import { config } from "../config.js";
29
+
30
+ // config.permissionsFile, for the reason in src/config.js: under a test run
31
+ // the writable copy must not be the developer's own. Defensive fallback for the
32
+ // tests that replace the config object with only the keys they read.
33
+ const FILE = config.permissionsFile
34
+ || path.join(config.projectRoot || process.cwd(), ".permissions.json");
35
+ const KEY = "_commandApprovals";
36
+
37
+ let cache = null;
38
+
39
+ function load() {
40
+ if (cache) return cache;
41
+ cache = {};
42
+ try {
43
+ if (existsSync(FILE)) {
44
+ const raw = JSON.parse(readFileSync(FILE, "utf8"));
45
+ const saved = raw[KEY];
46
+ if (saved && typeof saved === "object") cache = saved;
47
+ }
48
+ } catch {
49
+ // An unreadable or corrupt file must not take the permission system down.
50
+ // The failure mode is one extra prompt, which is the safe direction.
51
+ cache = {};
52
+ }
53
+ return cache;
54
+ }
55
+
56
+ function persist() {
57
+ try {
58
+ const raw = existsSync(FILE) ? JSON.parse(readFileSync(FILE, "utf8")) : {};
59
+ raw[KEY] = cache;
60
+ writeFileSync(FILE, JSON.stringify(raw, null, 2) + "\n");
61
+ } catch {}
62
+ }
63
+
64
+ /**
65
+ * The project a command was approved in.
66
+ *
67
+ * One place decides what "the project" is, because the caller that writes a
68
+ * grant and the caller that reads one have to agree exactly — when they do not,
69
+ * an "[a]lways" silently becomes a prompt again, which is what the owner read
70
+ * on 2026-09-29 as "[a]lways did nothing".
71
+ *
72
+ * An explicit cwd wins, since that is the agent stating where it is working.
73
+ * Failing that, the session workdir, which is set once at boot and is the
74
+ * project the operator is sitting in. Only then the process cwd.
75
+ */
76
+ function projectOf(cwd) {
77
+ const dir = cwd || process.env._FLINT_WORKDIR || process.cwd() || config.projectRoot;
78
+ return path.normalize(path.resolve(dir));
79
+ }
80
+
81
+ /**
82
+ * What the grant is about: the program being run, and its subcommand.
83
+ *
84
+ * The first two words, not the whole command line and not just the first word.
85
+ * Both extremes are wrong and both were tried:
86
+ *
87
+ * argv[0] only — a grant for `npm test` then covered `npm publish`, which is
88
+ * the opposite of what the operator agreed to. This is what
89
+ * the first version of this file did, and the test that
90
+ * caught it is the reason the subcommand is here.
91
+ * whole line — approving `npm test` does not then cover `npm test -- --watch`,
92
+ * so the operator answers the same question again over a flag.
93
+ *
94
+ * A leading path is the whole program: `./scripts/deploy.sh` is one specific
95
+ * file, and taking its next word as a subcommand would key the grant on
96
+ * whatever that script happens to be called.
97
+ *
98
+ * Known coarseness, stated rather than hidden: the second word is taken
99
+ * literally, so a grant for `git -C` covers other `git -C` invocations, and one
100
+ * for `npm` alone covers only bare `npm`. Both are over-grants in a narrow
101
+ * corner, and the alternative — parsing every tool's flag grammar — is a worse
102
+ * trade than a prompt that occasionally reappears.
103
+ *
104
+ * @returns {string|null} null for an empty command, which is "no grant applies"
105
+ */
106
+ export function commandProgram(command) {
107
+ if (!command || typeof command !== "string") return null;
108
+ const trimmed = command.trim();
109
+ if (!trimmed) return null;
110
+ const m = trimmed.match(/^"([^"]+)"|^'([^']+)'|^(\S+)/);
111
+ if (!m) return null;
112
+ const program = m[1] || m[2] || m[3] || null;
113
+ if (!program) return null;
114
+ // A path is the whole program — see above.
115
+ if (/[\\/]/.test(program)) return program;
116
+ const sub = trimmed.slice(m[0].length).trim().match(/^(\S+)/);
117
+ return sub ? `${program} ${sub[1]}` : program;
118
+ }
119
+
120
+ /** The key a grant is stored under. */
121
+ export function approvalKey({ cwd, command } = {}) {
122
+ const program = commandProgram(command);
123
+ if (!program) return null;
124
+ return `${projectOf(cwd)}\u0000${program}`;
125
+ }
126
+
127
+ /**
128
+ * Has this command already been approved in this project?
129
+ *
130
+ * Only the program is compared, which is why the grant is deliberately coarse:
131
+ * the alternative is storing whole command lines, where approving one invocation
132
+ * does not generalise and the operator answers the same question again on the
133
+ * next flag change. The subcommand is kept on purpose — `npm test` and
134
+ * `npm publish` are different decisions — while flags are not, because they
135
+ * select behaviour within one decision and are the thing the operator does not
136
+ * want to re-approve.
137
+ */
138
+ export function getCommandApproval({ cwd, command } = {}) {
139
+ const key = approvalKey({ cwd, command });
140
+ if (!key) return false;
141
+ return Boolean(load()[key]);
142
+ }
143
+
144
+ /**
145
+ * Remember an approval for this command in this project.
146
+ *
147
+ * @returns {string|null} the key stored, or null if there was nothing to store
148
+ */
149
+ export function grantCommandApproval({ cwd, command } = {}) {
150
+ const key = approvalKey({ cwd, command });
151
+ if (!key) return null;
152
+ load()[key] = true;
153
+ persist();
154
+ return key;
155
+ }
156
+
157
+ /** Forget one approval. Used by /permissions and by tests. */
158
+ export function revokeCommandApproval({ cwd, command } = {}) {
159
+ const key = approvalKey({ cwd, command });
160
+ if (!key || !load()[key]) return false;
161
+ delete cache[key];
162
+ persist();
163
+ return true;
164
+ }
165
+
166
+ /** Every grant, for display. */
167
+ export function listCommandApprovals() {
168
+ return { ...load() };
169
+ }
170
+
171
+ /**
172
+ * Drop the in-memory copy, so the next read comes from disk.
173
+ *
174
+ * Exported for tests, which need to prove an approval survived a restart. A
175
+ * test that could not simulate a restart cannot check that it survived one, and
176
+ * that is the property that was broken.
177
+ */
178
+ export function _resetForTest() {
179
+ cache = null;
180
+ }
@@ -0,0 +1,50 @@
1
+ // Dataset navigation tool — lets the model page through stored datasets
2
+
3
+ export const datasetToolDefs = [
4
+ {
5
+ type: "function",
6
+ function: {
7
+ name: "show_dataset",
8
+ description: "Navigate to a specific page of a named dataset. Datasets are created when tools return large result sets. Check 'Active datasets' in your context for available IDs.",
9
+ parameters: {
10
+ type: "object",
11
+ properties: {
12
+ id: { type: "string", description: "Dataset ID (e.g. ds_1)" },
13
+ page: { type: "number", description: "Page number (1-based). Omit to go to next page." },
14
+ },
15
+ required: ["id"],
16
+ },
17
+ },
18
+ },
19
+ ];
20
+
21
+ export function createDatasetHandlers(store) {
22
+ return {
23
+ show_dataset({ id, page }) {
24
+ const ds = store.getState().datasets[id];
25
+ if (!ds) {
26
+ const available = Object.values(store.getState().datasets)
27
+ .map((d) => `${d.id} "${d.label}"`)
28
+ .join(", ");
29
+ return `Error: dataset "${id}" not found. Available: ${available || "none"}`;
30
+ }
31
+
32
+ const totalPages = Math.ceil(ds.rows.length / ds.pageSize);
33
+ const target = page || ds.page + 1;
34
+ const clamped = Math.max(1, Math.min(target, totalPages));
35
+ store.getState().setDatasetPage(id, clamped);
36
+
37
+ const start = (clamped - 1) * ds.pageSize;
38
+ const pageRows = ds.rows.slice(start, start + ds.pageSize);
39
+
40
+ return {
41
+ _table: true,
42
+ _pagination: true, // skip re-registration as new dataset
43
+ columns: ds.columns,
44
+ rows: pageRows,
45
+ title: `${ds.label} (page ${clamped}/${totalPages}, ${ds.rows.length} total)`,
46
+ name: ds.label,
47
+ };
48
+ },
49
+ };
50
+ }