@mocito/pi-goal 0.1.0 → 0.1.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.2
4
+
5
+ - Move the extension entry point to `extensions/index.ts` so Pi displays the package extension compactly.
6
+
7
+ ## 0.1.1
8
+
9
+ - Add best-effort install/update telemetry, gated by Pi telemetry/offline settings and disabled in CI.
10
+
3
11
  ## 0.1.0
4
12
 
5
13
  - Initial `@mocito/pi-goal` package with `/goal`, goal tools, branch-scoped session persistence, usage accounting, UI status, and automatic continuation.
package/CONTRIBUTING.md CHANGED
@@ -43,7 +43,7 @@ Before opening a pull request:
43
43
 
44
44
  ## Coding guidelines
45
45
 
46
- - Keep `extensions/pi-goal/index.ts` focused on Pi extension wiring and move reusable implementation details into `src/`.
46
+ - Keep `extensions/index.ts` focused on Pi extension wiring and move reusable implementation details into `src/`.
47
47
  - When changing model tool parameters, update the Typebox schema, runtime validation, README tool docs, and tests together.
48
48
  - Treat persisted mutation shapes, custom message types, command names, and tool names as public interface.
49
49
  - Preserve branch-scoped semantics: reconstruct state from `ctx.sessionManager.getBranch()` and avoid global process state that crosses sessions unexpectedly.
package/README.md CHANGED
@@ -13,7 +13,7 @@ For local development:
13
13
  ```bash
14
14
  pi -e ./packages/pi-goal
15
15
  # or
16
- pi -e ./packages/pi-goal/extensions/pi-goal/index.ts
16
+ pi -e ./packages/pi-goal/extensions/index.ts
17
17
  ```
18
18
 
19
19
  ## Commands
@@ -69,7 +69,14 @@ Branch behavior: goal mutations are stored on the current session branch. If you
69
69
 
70
70
  ## Configuration
71
71
 
72
- v1 has no user-facing configuration. Automatic continuation is enabled for active goals and stops when the goal is paused, blocked, complete, usage-limited, budget-limited, cleared, or when pending user messages exist.
72
+ v1 has no user-facing goal configuration. Automatic continuation is enabled for active goals and stops when the goal is paused, blocked, complete, usage-limited, budget-limited, cleared, or when pending user messages exist.
73
+
74
+ Environment flags:
75
+
76
+ | Flag | Description |
77
+ | --- | --- |
78
+ | `PI_OFFLINE=1` | Disables install/update telemetry. |
79
+ | `PI_TELEMETRY=0` | Disables install/update telemetry. |
73
80
 
74
81
  ## Troubleshooting
75
82
 
@@ -120,4 +127,6 @@ Use the project-local release command from the repository root when possible:
120
127
 
121
128
  Pi packages execute arbitrary code with your user permissions. Install only from sources you trust.
122
129
 
123
- `pi-goal` does not require API keys, does not read provider credentials, and does not make network requests. Goal state is stored in local Pi session entries and goal objectives may be sent to the active model as hidden continuation context. Do not put secrets, credentials, tokens, or private data into goal objectives. See [SECURITY.md](./SECURITY.md) for the full security model and reporting instructions.
130
+ `pi-goal` does not require API keys and does not read provider credentials. Goal state is stored in local Pi session entries and goal objectives may be sent to the active model as hidden continuation context. Do not put secrets, credentials, tokens, or private data into goal objectives.
131
+
132
+ On startup, Pi Goal sends a best-effort install/update telemetry ping once per package version unless Pi telemetry is disabled, offline mode is enabled, or Pi runs in CI. See [SECURITY.md](./SECURITY.md) for the full security model and reporting instructions.
package/SECURITY.md CHANGED
@@ -23,6 +23,8 @@ The maintainer will acknowledge reports as soon as practical and coordinate disc
23
23
 
24
24
  `pi-goal` stores goal state as custom entries in the current Pi session branch. Goal objectives and usage summaries may appear in local Pi session files and in hidden continuation context sent to the active model. Do not put secrets, credentials, tokens, or private data into goal objectives.
25
25
 
26
- The package does not require API keys, does not read provider credentials, does not make network requests, and does not write files outside Pi's normal session storage. Its model tools can only inspect the current goal, create a new explicitly requested goal when none exists, or mark the current goal `complete`/`blocked`.
26
+ The package does not require API keys and does not read provider credentials. Its model tools can only inspect the current goal, create a new explicitly requested goal when none exists, or mark the current goal `complete`/`blocked`.
27
+
28
+ On startup, the extension sends a best-effort install/update telemetry ping to `mocito.dev` once per package version unless Pi telemetry is disabled, offline mode is enabled, or Pi runs in CI. The ping includes only the package name, version, and parsed platform/runtime/architecture from its User-Agent; it does not include prompts, goal objectives, file paths, session data, config values, or API keys. Telemetry writes a local deduplication marker under Pi's agent extensions directory.
27
29
 
28
30
  Goal objectives are treated as untrusted user-provided task data when continuation context is built. They are JSON-encoded before being embedded in the hidden context message to reduce prompt-injection risk from delimiter-breaking text.
@@ -1,16 +1,19 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { accountUsageFromBranch, isBudgetExceeded } from "../../src/accounting.js";
3
- import { goalCompletions, handleGoalCommand } from "../../src/commands.js";
4
- import { filterGoalContextMessages, GoalContinuationScheduler } from "../../src/continuation.js";
5
- import { registerGoalRenderers } from "../../src/rendering.js";
6
- import { appendGoalMutation, applyGoalMutation, reconstructGoalState, statusMutation } from "../../src/state.js";
7
- import { registerGoalTools } from "../../src/tools.js";
8
- import type { GoalState } from "../../src/types.js";
9
- import { GOAL_EVENT_TYPE } from "../../src/types.js";
10
- import { nowIso, realizedTimeUsed } from "../../src/utils.js";
11
- import { clearGoalUi, updateGoalUi } from "../../src/ui.js";
2
+ import { accountUsageFromBranch, isBudgetExceeded } from "../src/accounting.js";
3
+ import { goalCompletions, handleGoalCommand } from "../src/commands.js";
4
+ import { filterGoalContextMessages, GoalContinuationScheduler } from "../src/continuation.js";
5
+ import { registerGoalRenderers } from "../src/rendering.js";
6
+ import { appendGoalMutation, applyGoalMutation, reconstructGoalState, statusMutation } from "../src/state.js";
7
+ import { reportInstallTelemetry } from "../src/install-telemetry.js";
8
+ import { registerGoalTools } from "../src/tools.js";
9
+ import type { GoalState } from "../src/types.js";
10
+ import { GOAL_EVENT_TYPE } from "../src/types.js";
11
+ import { nowIso, realizedTimeUsed } from "../src/utils.js";
12
+ import { clearGoalUi, updateGoalUi } from "../src/ui.js";
12
13
 
13
14
  export default function piGoal(pi: ExtensionAPI) {
15
+ reportInstallTelemetry();
16
+
14
17
  let goal: GoalState | null = null;
15
18
  const scheduler = new GoalContinuationScheduler(pi, { getGoal: () => goal });
16
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mocito/pi-goal",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Persistent long-running goals for Pi.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "pi": {
28
28
  "extensions": [
29
- "./extensions/pi-goal/index.ts"
29
+ "./extensions/index.ts"
30
30
  ]
31
31
  },
32
32
  "files": [
package/src/index.ts CHANGED
@@ -9,3 +9,4 @@ export * from "./continuation.js";
9
9
  export * from "./tools.js";
10
10
  export * from "./ui.js";
11
11
  export * from "./rendering.js";
12
+ export * from "./install-telemetry.js";
@@ -0,0 +1,104 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
6
+
7
+ const PACKAGE_NAME = "pi-goal";
8
+ const INSTALL_TELEMETRY_URL = "https://mocito.dev/api/report-install";
9
+ const INSTALL_TELEMETRY_TIMEOUT_MS = 5000;
10
+ const CI_ENVIRONMENT_VARIABLES = [
11
+ "APPVEYOR",
12
+ "BITBUCKET_BUILD_NUMBER",
13
+ "BUILDKITE",
14
+ "CIRCLECI",
15
+ "CODESPACES",
16
+ "DRONE",
17
+ "GITHUB_ACTIONS",
18
+ "GITLAB_CI",
19
+ "JENKINS_URL",
20
+ "NETLIFY",
21
+ "TEAMCITY_VERSION",
22
+ "TF_BUILD",
23
+ "TRAVIS",
24
+ "VERCEL",
25
+ ];
26
+
27
+ interface InstallTelemetryState {
28
+ lastReportedVersion?: string;
29
+ }
30
+
31
+ interface PiSettingsDocument {
32
+ enableInstallTelemetry?: unknown;
33
+ }
34
+
35
+ function readJsonFile(path: string): unknown {
36
+ try {
37
+ return JSON.parse(readFileSync(path, "utf8")) as unknown;
38
+ } catch {
39
+ return {};
40
+ }
41
+ }
42
+
43
+ function isTruthyEnvFlag(value: string | undefined): boolean {
44
+ if (!value) return false;
45
+ return value === "1" || value.toLowerCase() === "true" || value.toLowerCase() === "yes";
46
+ }
47
+
48
+ function isPresentEnvFlag(value: string | undefined): boolean {
49
+ if (!value) return false;
50
+ const normalized = value.toLowerCase();
51
+ return normalized !== "0" && normalized !== "false" && normalized !== "no";
52
+ }
53
+
54
+ function isCiEnvironment(): boolean {
55
+ if (isTruthyEnvFlag(process.env.CI)) return true;
56
+ return CI_ENVIRONMENT_VARIABLES.some((name) => isPresentEnvFlag(process.env[name]));
57
+ }
58
+
59
+ function isInstallTelemetryEnabled(): boolean {
60
+ if (isCiEnvironment()) return false;
61
+ if (isTruthyEnvFlag(process.env.PI_OFFLINE)) return false;
62
+ if (process.env.PI_TELEMETRY !== undefined) return isTruthyEnvFlag(process.env.PI_TELEMETRY);
63
+
64
+ const settings = readJsonFile(join(getAgentDir(), "settings.json")) as PiSettingsDocument;
65
+ return settings.enableInstallTelemetry !== false;
66
+ }
67
+
68
+ function getPackageVersion(): string {
69
+ const packageJson = readJsonFile(fileURLToPath(new URL("../package.json", import.meta.url))) as { version?: unknown };
70
+ return typeof packageJson.version === "string" && packageJson.version.length > 0 ? packageJson.version : "0.0.0";
71
+ }
72
+
73
+ function getInstallTelemetryUserAgent(version: string): string {
74
+ const runtimeVersions = process.versions as NodeJS.ProcessVersions & { bun?: string };
75
+ const runtime = runtimeVersions.bun ? `bun/${runtimeVersions.bun}` : `node/${process.version}`;
76
+ return `${PACKAGE_NAME}/${version} (${process.platform}; ${runtime}; ${process.arch})`;
77
+ }
78
+
79
+ async function reportInstallTelemetryAsync(): Promise<void> {
80
+ try {
81
+ if (!isInstallTelemetryEnabled()) return;
82
+
83
+ const version = getPackageVersion();
84
+ const extensionsDir = join(getAgentDir(), "extensions");
85
+ const statePath = join(extensionsDir, "pi-goal-install.json");
86
+ const state = readJsonFile(statePath) as InstallTelemetryState;
87
+ if (state.lastReportedVersion === version) return;
88
+
89
+ await mkdir(extensionsDir, { recursive: true });
90
+ await writeFile(statePath, `${JSON.stringify({ lastReportedVersion: version }, null, 2)}\n`, "utf8");
91
+
92
+ const params = new URLSearchParams({ tool: PACKAGE_NAME, version });
93
+ await fetch(`${INSTALL_TELEMETRY_URL}?${params.toString()}`, {
94
+ headers: { "User-Agent": getInstallTelemetryUserAgent(version) },
95
+ signal: AbortSignal.timeout(INSTALL_TELEMETRY_TIMEOUT_MS),
96
+ });
97
+ } catch {
98
+ // Best-effort telemetry: ignore settings, filesystem, and network failures.
99
+ }
100
+ }
101
+
102
+ export function reportInstallTelemetry(): void {
103
+ void reportInstallTelemetryAsync();
104
+ }