@frockbot/plugin-audit 0.3.5 → 0.3.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frockbot/plugin-audit",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -25,11 +25,11 @@
25
25
  "typecheck": "vue-tsc --noEmit -p tsconfig.json"
26
26
  },
27
27
  "dependencies": {
28
- "@frockbot/client-core": "0.3.5",
29
- "@frockbot/client-ui": "0.3.5",
30
- "@frockbot/kernel-contracts": "0.3.5",
31
- "@frockbot/plugin-shell": "0.3.5",
32
- "@frockbot/secret-shapes": "0.3.5",
28
+ "@frockbot/client-core": "0.3.7",
29
+ "@frockbot/client-ui": "0.3.7",
30
+ "@frockbot/kernel-contracts": "0.3.7",
31
+ "@frockbot/plugin-shell": "0.3.7",
32
+ "@frockbot/secret-shapes": "0.3.7",
33
33
  "cordis": "4.0.0-rc.8",
34
34
  "vue": "3.5.41"
35
35
  },
@@ -27,9 +27,11 @@ describe("the classifier table", () => {
27
27
  "computer",
28
28
  ],
29
29
  ["computer_process_poll", { processId: "p1" }, "process", "computer"],
30
- ["memory_write", { text: "a fact" }, "file", "computer"],
31
- ["skill_write", { path: "a.md" }, "file", "computer"],
32
- ["package_author", { packageId: "x" }, "file", "computer"],
30
+ // Memory and Skills are the Workspace, not the Computer: they are
31
+ // written while it is hibernated and by Bots that have none.
32
+ ["memory_write", { text: "a fact" }, "file", "workspace"],
33
+ ["skill_write", { path: "a.md" }, "file", "workspace"],
34
+ ["package_author", { packageId: "x" }, "file", "workspace"],
33
35
  ["mcp__example__echo", { message: "hi" }, "mcp", "remote:example"],
34
36
  ["mcp__beeper__send_message", {}, "mcp", "remote:beeper"],
35
37
  // Read-only and product tools perform no audited effect at all. An audit
@@ -139,7 +141,8 @@ describe("the classifier table", () => {
139
141
  toolName: "package_author",
140
142
  input: { packageId: "acme" },
141
143
  }),
142
- ).toEqual({ kind: "file", target: "computer" });
144
+ // `package_author` writes the Workspace, not the Computer.
145
+ ).toEqual({ kind: "file", target: "workspace" });
143
146
  // A wrapper that names nothing resolvable stays the wrapper, and the
144
147
  // wrapper is not an audited effect.
145
148
  expect(auditKindForToolV1("call_dynamic_tool", {})).toBeUndefined();
package/src/classify.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  AUDIT_TARGET_COMPUTER_V1,
20
20
  AUDIT_TARGET_MACHINE_PREFIX_V1,
21
21
  AUDIT_TARGET_REMOTE_PREFIX_V1,
22
+ AUDIT_TARGET_WORKSPACE_V1,
22
23
  type AuditKindV1,
23
24
  } from "./shared.js";
24
25
 
@@ -41,10 +42,6 @@ export interface AuditClassificationV1 {
41
42
  * costs one table row and no new authority.
42
43
  */
43
44
  const FILE_TOOLS = new Set([
44
- "memory_write",
45
- "memory_forget",
46
- "skill_write",
47
- "package_author",
48
45
  // The registered machine's file verbs (register rows 48, 49). They are file
49
46
  // effects on "a separate filesystem" — the User's own laptop — which is why
50
47
  // their target is `machine:<id>` and never `computer`.
@@ -53,6 +50,21 @@ const FILE_TOOLS = new Set([
53
50
  "machine_copy_from_computer",
54
51
  ]);
55
52
 
53
+ /**
54
+ * File effects that land in the User's Workspace, not on the Computer.
55
+ *
56
+ * Memory and Skills are object storage: they are written while the Computer is
57
+ * hibernated, and by Bots whose User has no Computer configured at all. Their
58
+ * rows said "This Computer", which named the wrong place and, in the case we
59
+ * saw, a place that did not exist.
60
+ */
61
+ const WORKSPACE_FILE_TOOLS = new Set([
62
+ "memory_write",
63
+ "memory_forget",
64
+ "skill_write",
65
+ "package_author",
66
+ ]);
67
+
56
68
  /**
57
69
  * The registered machine's shell verb.
58
70
  *
@@ -192,6 +204,9 @@ export function auditKindForToolV1(
192
204
  if (name.startsWith(MACHINE_MESSAGES_PREFIX)) {
193
205
  return { kind: "mcp", target: onComputer };
194
206
  }
207
+ if (WORKSPACE_FILE_TOOLS.has(name)) {
208
+ return { kind: "file", target: AUDIT_TARGET_WORKSPACE_V1 };
209
+ }
195
210
  if (FILE_TOOLS.has(name)) return { kind: "file", target: onComputer };
196
211
  const mcp = MCP_TOOL.exec(name);
197
212
  if (mcp) {
@@ -56,6 +56,8 @@ function selectKind(kind: AuditKindV1 | undefined): void {
56
56
  /** Where the effect ran, in words rather than in the wire shape. */
57
57
  function target(entry: AuditEntryV1): string {
58
58
  if (entry.target === "computer") return "This Computer";
59
+ // Memory and Skills are files in the Workspace, not on the Computer.
60
+ if (entry.target === "workspace") return "Workspace";
59
61
  if (entry.target.startsWith("machine:")) {
60
62
  return `Machine ${entry.target.slice("machine:".length)}`;
61
63
  }
@@ -83,9 +85,8 @@ function when(entry: AuditEntryV1): string {
83
85
  <span class="audit__intro">
84
86
  <strong>Audit log</strong>
85
87
  <small>
86
- Every shell command, browser action, remote tool call and Workspace
87
- write this Bot has made. Arguments are never kept — only a digest and
88
- a redacted preview.
88
+ Every shell command, browser action, remote tool call and file write
89
+ this Bot has made. Command details aren't stored.
89
90
  </small>
90
91
  </span>
91
92
  <UiButton
@@ -106,17 +107,15 @@ function when(entry: AuditEntryV1): string {
106
107
  class="audit__banner"
107
108
  role="status"
108
109
  >
109
- Older activity has been trimmed to this account's retention bound — 20 000
110
- entries, or 180 days. Rebuilding restores everything the Bots' own turns
111
- still hold.
110
+ Older activity has been trimmed. Rebuild to restore what's still
111
+ available.
112
112
  </p>
113
113
  <p
114
114
  v-else-if="audit.indexState === 'rebuilding'"
115
115
  class="audit__banner"
116
116
  role="status"
117
117
  >
118
- A rebuild is in progress. What is shown may be incomplete until it
119
- finishes.
118
+ Rebuilding. What's shown may be incomplete until it finishes.
120
119
  </p>
121
120
 
122
121
  <div v-if="audit.receipt" class="audit-receipt">
@@ -125,10 +124,9 @@ function when(entry: AuditEntryV1): string {
125
124
  {{ audit.receipt.bots }} Bots
126
125
  </strong>
127
126
  <small>
128
- {{ audit.receipt.unknownOutcomes }} with an outcome the turn log cannot
129
- explain, and {{ audit.receipt.hostJournalDiscrepancies }} effect(s) the
130
- Computer host reported that no turn accounts for. A discrepancy is
131
- counted here, never written in as if a turn had recorded it.
127
+ {{ audit.receipt.unknownOutcomes }} with an outcome this log can't
128
+ explain, and {{ audit.receipt.hostJournalDiscrepancies }} action(s) the
129
+ computer reported that this log can't match to anything the Bot did.
132
130
  </small>
133
131
  <div class="audit-receipt__actions">
134
132
  <UiButton type="button" @click="audit.dismissReceipt()">
@@ -159,7 +157,7 @@ function when(entry: AuditEntryV1): string {
159
157
  </div>
160
158
 
161
159
  <p v-if="audit.loaded && audit.entries.length === 0" class="audit__empty">
162
- No audited activity yet. A turn that only talks records nothing here.
160
+ Nothing here yet. Only actions get logged, not conversation.
163
161
  </p>
164
162
 
165
163
  <ol v-else class="audit__rows">
package/src/shared.ts CHANGED
@@ -88,6 +88,16 @@ export const AUDIT_OUTCOMES_V1: readonly AuditOutcomeV1[] = [
88
88
 
89
89
  /** The Bot's own Computer — the default target for every Computer effect. */
90
90
  export const AUDIT_TARGET_COMPUTER_V1 = "computer";
91
+ /**
92
+ * The User's Workspace in object storage.
93
+ *
94
+ * Memory and Skills live here, not on the Computer, and they are written
95
+ * while it is hibernated — a Bot with no Computer configured at all still
96
+ * writes Memory. Rows for those writes said "This Computer", which named a
97
+ * machine that had nothing to do with the effect and, in the case we saw, did
98
+ * not exist.
99
+ */
100
+ export const AUDIT_TARGET_WORKSPACE_V1 = "workspace";
91
101
  /** A registered machine of the User's, `machine:<id>` (parity register §2.16). */
92
102
  export const AUDIT_TARGET_MACHINE_PREFIX_V1 = "machine:";
93
103
  /** A remote MCP server, `remote:<host>`. */
@@ -269,6 +279,7 @@ export function decodeAuditOccurrenceIdV1(value: unknown): {
269
279
  /** Whether a string is one of the three target shapes this schema allows. */
270
280
  export function isAuditTargetV1(value: string): boolean {
271
281
  if (value === AUDIT_TARGET_COMPUTER_V1) return true;
282
+ if (value === AUDIT_TARGET_WORKSPACE_V1) return true;
272
283
  if (value.length > MAX_TARGET_LENGTH) return false;
273
284
  if (value.startsWith(AUDIT_TARGET_MACHINE_PREFIX_V1)) {
274
285
  return /^machine:[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value);